Skip to content

Commit 9315d62

Browse files
authored
Support type objects in functools.partial (#17292)
1 parent fa2aefc commit 9315d62

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

mypy/checker.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,11 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab
681681
inner_type = get_proper_type(inner_type)
682682
outer_type: CallableType | None = None
683683
if inner_type is not None and not isinstance(inner_type, AnyType):
684+
if isinstance(inner_type, TypeType):
685+
if isinstance(inner_type.item, Instance):
686+
inner_type = expand_type_by_instance(
687+
type_object_type(inner_type.item.type, self.named_type), inner_type.item
688+
)
684689
if isinstance(inner_type, CallableType):
685690
outer_type = inner_type
686691
elif isinstance(inner_type, Instance):

test-data/unit/check-functools.test

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,12 +303,12 @@ p(1) # E: Argument 1 to "A" has incompatible type "int"; expected "str"
303303
p(z=1) # E: Unexpected keyword argument "z" for "A"
304304

305305
def main(t: Type[A]) -> None:
306-
p = functools.partial(t, 1) # E: "Type[A]" not callable
306+
p = functools.partial(t, 1)
307307
reveal_type(p) # N: Revealed type is "functools.partial[__main__.A]"
308308

309309
p("a") # OK
310-
p(1) # False negative
311-
p(z=1) # False negative
310+
p(1) # E: Argument 1 to "A" has incompatible type "int"; expected "str"
311+
p(z=1) # E: Unexpected keyword argument "z" for "A"
312312

313313
[builtins fixtures/dict.pyi]
314314

@@ -346,3 +346,29 @@ reveal_type(functools.partial(fn3, 2)()) # E: "str" not callable \
346346
# N: Revealed type is "builtins.int" \
347347
# E: Argument 1 to "partial" has incompatible type "Union[Callable[[int], int], str]"; expected "Callable[..., int]"
348348
[builtins fixtures/tuple.pyi]
349+
350+
[case testFunctoolsPartialTypeObject]
351+
import functools
352+
from typing import Type, Generic, TypeVar
353+
354+
class A:
355+
def __init__(self, val: int) -> None: ...
356+
357+
cls1: Type[A]
358+
reveal_type(functools.partial(cls1, 2)()) # N: Revealed type is "__main__.A"
359+
functools.partial(cls1, "asdf") # E: Argument 1 to "A" has incompatible type "str"; expected "int"
360+
361+
T = TypeVar("T")
362+
class B(Generic[T]):
363+
def __init__(self, val: T) -> None: ...
364+
365+
cls2: Type[B[int]]
366+
reveal_type(functools.partial(cls2, 2)()) # N: Revealed type is "__main__.B[builtins.int]"
367+
functools.partial(cls2, "asdf") # E: Argument 1 to "B" has incompatible type "str"; expected "int"
368+
369+
def foo(cls3: Type[B[T]]):
370+
reveal_type(functools.partial(cls3, "asdf")) # N: Revealed type is "functools.partial[__main__.B[T`-1]]" \
371+
# E: Argument 1 to "B" has incompatible type "str"; expected "T"
372+
reveal_type(functools.partial(cls3, 2)()) # N: Revealed type is "__main__.B[T`-1]" \
373+
# E: Argument 1 to "B" has incompatible type "int"; expected "T"
374+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)