diff --git a/crates/ty_ide/src/inlay_hints.rs b/crates/ty_ide/src/inlay_hints.rs index 392e682689c73..07d667c530541 100644 --- a/crates/ty_ide/src/inlay_hints.rs +++ b/crates/ty_ide/src/inlay_hints.rs @@ -7235,21 +7235,20 @@ mod tests { ); assert_snapshot!(test.inlay_hints(), @r#" - def f(xyxy: object): if isinstance(xyxy, list): x[: Top[list[Unknown]]] = xyxy --------------------------------------------- info[inlay-hint-location]: Inlay Hint Target - --> stdlib/ty_extensions.pyi:24:1 + --> stdlib/ty_extensions.pyi:44:1 | - 22 | CallableTypeOf: _SpecialForm - 23 | - 24 | Top: _SpecialForm + 42 | """ + 43 | + 44 | Top: _SpecialForm | ^^^ - 25 | """ - 26 | `Top[T]` represents the "top materialization" of `T`. + 45 | """ + 46 | `Top[T]` represents the "top materialization" of `T`. | info: Source --> main2.py:4:13 diff --git a/crates/ty_python_semantic/resources/corpus/ty_extensions.py b/crates/ty_python_semantic/resources/corpus/ty_extensions.py index 64810a630764f..df3bcc15b15e6 100644 --- a/crates/ty_python_semantic/resources/corpus/ty_extensions.py +++ b/crates/ty_python_semantic/resources/corpus/ty_extensions.py @@ -5,7 +5,13 @@ This is a regression test for https://github.com/astral-sh/ty/issues/366 """ -from ty_extensions import CallableTypeOf, Intersection, Not, TypeOf +from ty_extensions import ( + CallableTypeOf, + Intersection, + Not, + RegularCallableTypeOf, + TypeOf, +) class A: ... @@ -28,3 +34,7 @@ def _(x: TypeOf[1j]): def _(x: CallableTypeOf[str]): pass + + +def _(x: RegularCallableTypeOf[str]): + pass diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/callable.md b/crates/ty_python_semantic/resources/mdtest/annotations/callable.md index b499255a007cf..920a953622271 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/callable.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/callable.md @@ -516,7 +516,7 @@ def f_okay(c: Callable[[], None]): ### Subclasses should return themselves, not superclass ```py -from ty_extensions import into_callable +from ty_extensions import into_regular_callable class Base: def __init__(self) -> None: @@ -526,7 +526,7 @@ class A(Base): pass # revealed: () -> A -reveal_type(into_callable(A)) +reveal_type(into_regular_callable(A)) ``` [gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md b/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md index 55ea8e59f1513..d606bd18b6538 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md @@ -84,11 +84,11 @@ reveal_type(Baz.__supertype__) # revealed: type | NewType ```py from collections.abc import Callable from typing_extensions import NewType -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf Foo = NewType("Foo", int) -def _(obj: CallableTypeOf[Foo]): +def _(obj: RegularCallableTypeOf[Foo]): reveal_type(obj) # revealed: (int, /) -> Foo def f(_: Callable[[int], Foo]): ... @@ -105,15 +105,15 @@ g(Foo) # error: [invalid-argument-type] ```py from typing import NewType, Callable, Any -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf N = NewType("N", int) i = N(42) y: Callable[..., Any] = i # error: [invalid-assignment] "Object of type `N` is not assignable to `(...) -> Any`" -# error: [invalid-type-form] "Expected the first argument to `ty_extensions.CallableTypeOf` to be a callable object, but got an object of type `N`" -def f(x: CallableTypeOf[i]): +# error: [invalid-type-form] "Expected the first argument to `ty_extensions.RegularCallableTypeOf` to be a callable object, but got an object of type `N`" +def f(x: RegularCallableTypeOf[i]): reveal_type(x) # revealed: Unknown class SomethingCallable: @@ -125,7 +125,7 @@ j = N2(SomethingCallable()) z: Callable[[str], bytes] = j # fine -def g(x: CallableTypeOf[j]): +def g(x: RegularCallableTypeOf[j]): reveal_type(x) # revealed: (a: str) -> bytes ``` diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index 1113bdf137ed2..e4d5940f237bb 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/self.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/self.md @@ -894,12 +894,12 @@ reveal_type(generic_context(C.f)) This makes sure that we don't bind `self` if it's not a positional parameter: ```py -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf class C: def method(*args, **kwargs) -> None: ... -def _(c: CallableTypeOf[C().method]): +def _(c: RegularCallableTypeOf[C().method]): reveal_type(c) # revealed: (...) -> None ``` diff --git a/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md b/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md index 1ca19c1a0ebca..471a4b5b8e5df 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md +++ b/crates/ty_python_semantic/resources/mdtest/call/callables_as_descriptors.md @@ -13,7 +13,7 @@ instance to the first argument. The bound-method object therefore has a differen the first argument: ```py -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf from typing import Callable class C1: @@ -21,8 +21,8 @@ class C1: return str(x) def _( - accessed_on_class: CallableTypeOf[C1.method], - accessed_on_instance: CallableTypeOf[C1().method], + accessed_on_class: RegularCallableTypeOf[C1.method], + accessed_on_instance: RegularCallableTypeOf[C1().method], ): reveal_type(accessed_on_class) # revealed: (self: C1, x: int) -> str reveal_type(accessed_on_instance) # revealed: (x: int) -> str @@ -41,8 +41,8 @@ class C2: non_descriptor_callable: NonDescriptorCallable2 = NonDescriptorCallable2() def _( - accessed_on_class: CallableTypeOf[C2.non_descriptor_callable], - accessed_on_instance: CallableTypeOf[C2().non_descriptor_callable], + accessed_on_class: RegularCallableTypeOf[C2.non_descriptor_callable], + accessed_on_instance: RegularCallableTypeOf[C2().non_descriptor_callable], ): reveal_type(accessed_on_class) # revealed: (c2: C2, x: int) -> str reveal_type(accessed_on_instance) # revealed: (c2: C2, x: int) -> str @@ -68,8 +68,8 @@ However, when they are accessed on instances of `C3`, they have different signat ```py def _( - method_accessed_on_instance: CallableTypeOf[C3().method], - callable_accessed_on_instance: CallableTypeOf[C3().non_descriptor_callable], + method_accessed_on_instance: RegularCallableTypeOf[C3().method], + callable_accessed_on_instance: RegularCallableTypeOf[C3().non_descriptor_callable], ): reveal_type(method_accessed_on_instance) # revealed: (x: int) -> str reveal_type(callable_accessed_on_instance) # revealed: (c3: C3, x: int) -> str @@ -301,7 +301,7 @@ The callable type of a type object is not function-like. ```py from typing import ClassVar -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf class WithNew: def __new__(self, x: int) -> WithNew: @@ -312,8 +312,8 @@ class WithInit: pass class C: - with_new: ClassVar[CallableTypeOf[WithNew]] - with_init: ClassVar[CallableTypeOf[WithInit]] + with_new: ClassVar[RegularCallableTypeOf[WithNew]] + with_init: ClassVar[RegularCallableTypeOf[WithInit]] C.with_new(1) C().with_new(1) diff --git a/crates/ty_python_semantic/resources/mdtest/call/methods.md b/crates/ty_python_semantic/resources/mdtest/call/methods.md index d973b22d18cdd..986f9ce21d7c3 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/methods.md +++ b/crates/ty_python_semantic/resources/mdtest/call/methods.md @@ -848,7 +848,7 @@ properties are understood correctly for these functions and methods. ```py import types from typing import Callable -from ty_extensions import static_assert, CallableTypeOf, is_assignable_to, TypeOf +from ty_extensions import static_assert, RegularCallableTypeOf, is_assignable_to, TypeOf def f(obj: type) -> None: ... @@ -912,18 +912,18 @@ reveal_type("foo".startswith) static_assert(is_assignable_to(TypeOf["foo".startswith], Callable)) def _( - a: CallableTypeOf[types.FunctionType.__get__], - b: CallableTypeOf[f], - c: CallableTypeOf[f.__get__], - d: CallableTypeOf[types.FunctionType.__call__], - e: CallableTypeOf[f.__call__], - f: CallableTypeOf[property], - g: CallableTypeOf[property.__get__], - h: CallableTypeOf[MyClass.my_property.__get__], - i: CallableTypeOf[property.__set__], - j: CallableTypeOf[MyClass.my_property.__set__], - k: CallableTypeOf[str.startswith], - l: CallableTypeOf["foo".startswith], + a: RegularCallableTypeOf[types.FunctionType.__get__], + b: RegularCallableTypeOf[f], + c: RegularCallableTypeOf[f.__get__], + d: RegularCallableTypeOf[types.FunctionType.__call__], + e: RegularCallableTypeOf[f.__call__], + f: RegularCallableTypeOf[property], + g: RegularCallableTypeOf[property.__get__], + h: RegularCallableTypeOf[MyClass.my_property.__get__], + i: RegularCallableTypeOf[property.__set__], + j: RegularCallableTypeOf[MyClass.my_property.__set__], + k: RegularCallableTypeOf[str.startswith], + l: RegularCallableTypeOf["foo".startswith], ): # revealed: Overload[(self: FunctionType, instance: None, owner: type, /) -> Unknown, (self: FunctionType, instance: object, owner: type | None = None, /) -> Unknown] reveal_type(a) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/callables.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/callables.md index b305eeb333cbe..da958ee4ee149 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/callables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/callables.md @@ -45,28 +45,28 @@ reveal_type(C(1)) When we coerce a generic callable into a `Callable` type, it remembers that it is generic: ```py -from ty_extensions import into_callable +from ty_extensions import into_regular_callable # revealed: [T](t: T) -> T -reveal_type(into_callable(identity)) +reveal_type(into_regular_callable(identity)) # revealed: ty_extensions.GenericContext[T@identity] -reveal_type(generic_context(into_callable(identity))) +reveal_type(generic_context(into_regular_callable(identity))) # revealed: Literal[1] -reveal_type(into_callable(identity)(1)) +reveal_type(into_regular_callable(identity)(1)) # revealed: [**P, T](c: (**P) -> T) -> ((**P) -> T) -reveal_type(into_callable(identity2)) +reveal_type(into_regular_callable(identity2)) # revealed: ty_extensions.GenericContext[P@identity2, T@identity2] -reveal_type(generic_context(into_callable(identity2))) +reveal_type(generic_context(into_regular_callable(identity2))) # revealed: [T](t: T) -> T -reveal_type(into_callable(identity2)(identity)) +reveal_type(into_regular_callable(identity2)(identity)) # revealed: [T](t: T) -> C[T] -reveal_type(into_callable(C)) +reveal_type(into_regular_callable(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) # revealed: C[int] -reveal_type(into_callable(C)(1)) +reveal_type(into_regular_callable(C)(1)) ``` ## Naming a generic `Callable`: type aliases diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md index 5b7aff47a3bbc..44a43ca773d3a 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md @@ -402,7 +402,7 @@ consistent with each other. ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") @@ -413,7 +413,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -425,7 +425,7 @@ wrong_innards: C[int] = C("five") ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") @@ -435,7 +435,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -447,7 +447,7 @@ wrong_innards: C[int] = C("five") ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") @@ -460,7 +460,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -472,7 +472,7 @@ wrong_innards: C[int] = C("five") ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") @@ -485,7 +485,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -502,7 +502,7 @@ class D(Generic[T]): # revealed: ty_extensions.GenericContext[T@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(1)) # revealed: D[int] @@ -518,7 +518,7 @@ to specialize the class. ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U") @@ -534,7 +534,7 @@ class D(C[V, int]): # revealed: ty_extensions.GenericContext[V@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[V@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(1)) # revealed: D[int] ``` @@ -543,7 +543,7 @@ reveal_type(D(1)) # revealed: D[int] ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U") @@ -557,7 +557,7 @@ class D(C[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(C(1, "str")) # revealed: C[int, str] reveal_type(D(1, "str")) # revealed: D[int, str] @@ -569,7 +569,7 @@ This is a specific example of the above, since it was reported specifically by a ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U") @@ -580,7 +580,7 @@ class D(dict[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(key=1)) # revealed: D[str, int] ``` @@ -593,7 +593,7 @@ context. But from the user's point of view, this is another example of the above ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U") @@ -603,7 +603,7 @@ class C(tuple[T, U]): ... # revealed: ty_extensions.GenericContext[T@C, U@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, U@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C((1, 2))) # revealed: C[int, int] ``` @@ -636,7 +636,7 @@ def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: t ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable S = TypeVar("S") T = TypeVar("T") @@ -647,7 +647,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, S@__init__] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1, 1)) # revealed: C[int] reveal_type(C(1, "string")) # revealed: C[int] @@ -661,7 +661,7 @@ wrong_innards: C[int] = C("five", 1) ```py from typing_extensions import overload, Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U", covariant=True) @@ -680,7 +680,7 @@ class C(Generic[T]): # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C("string")) # revealed: C[str] reveal_type(C(b"bytes")) # revealed: C[bytes] @@ -712,7 +712,7 @@ class D(Generic[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D("string")) # revealed: D[str, Literal["string"]] reveal_type(D(1)) # revealed: D[str, Literal[1]] @@ -724,7 +724,7 @@ reveal_type(D(1, "string")) # revealed: D[int, Literal["string"]] ```py from dataclasses import dataclass from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") @@ -735,7 +735,7 @@ class A(Generic[T]): # revealed: ty_extensions.GenericContext[T@A] reveal_type(generic_context(A)) # revealed: ty_extensions.GenericContext[T@A] -reveal_type(generic_context(into_callable(A))) +reveal_type(generic_context(into_regular_callable(A))) reveal_type(A(x=1)) # revealed: A[int] ``` @@ -744,7 +744,7 @@ reveal_type(A(x=1)) # revealed: A[int] ```py from typing_extensions import Generic, TypeVar -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable T = TypeVar("T") U = TypeVar("U", default=T) @@ -754,7 +754,7 @@ class C(Generic[T, U]): ... # revealed: ty_extensions.GenericContext[T@C, U@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, U@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C()) # revealed: C[Unknown, Unknown] @@ -764,7 +764,7 @@ class D(Generic[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D()) # revealed: D[Unknown, Unknown] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/callables.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/callables.md index 1573d68aebec9..42300e67b0ed5 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/callables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/callables.md @@ -47,28 +47,28 @@ reveal_type(C(1)) When we coerce a generic callable into a `Callable` type, it remembers that it is generic: ```py -from ty_extensions import into_callable +from ty_extensions import into_regular_callable # revealed: [T](t: T) -> T -reveal_type(into_callable(identity)) +reveal_type(into_regular_callable(identity)) # revealed: ty_extensions.GenericContext[T@identity] -reveal_type(generic_context(into_callable(identity))) +reveal_type(generic_context(into_regular_callable(identity))) # revealed: Literal[1] -reveal_type(into_callable(identity)(1)) +reveal_type(into_regular_callable(identity)(1)) # revealed: [**P, T](c: (**P) -> T) -> ((**P) -> T) -reveal_type(into_callable(identity2)) +reveal_type(into_regular_callable(identity2)) # revealed: ty_extensions.GenericContext[P@identity2, T@identity2] -reveal_type(generic_context(into_callable(identity2))) +reveal_type(generic_context(into_regular_callable(identity2))) # revealed: [T](t: T) -> T -reveal_type(into_callable(identity2)(identity)) +reveal_type(into_regular_callable(identity2)(identity)) # revealed: [T](t: T) -> C[T] -reveal_type(into_callable(C)) +reveal_type(into_regular_callable(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) # revealed: C[int] -reveal_type(into_callable(C)(1)) +reveal_type(into_regular_callable(C)(1)) ``` ## Naming a generic `Callable`: type aliases diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md index 36bff77c0c54e..258b77a7f084a 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md @@ -286,7 +286,7 @@ signatures don't count towards variance). ### `__new__` only ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: x: T @@ -297,7 +297,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -308,7 +308,7 @@ wrong_innards: C[int] = C("five") ### `__init__` only ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: x: T @@ -318,7 +318,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -329,7 +329,7 @@ wrong_innards: C[int] = C("five") ### Identical `__new__` and `__init__` signatures ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: x: T @@ -342,7 +342,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -353,7 +353,7 @@ wrong_innards: C[int] = C("five") ### Compatible `__new__` and `__init__` signatures ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: x: T @@ -366,7 +366,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1)) # revealed: C[int] @@ -385,7 +385,7 @@ class D[T]: # revealed: ty_extensions.GenericContext[T@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(1)) # revealed: D[int] @@ -400,7 +400,7 @@ If either method comes from a generic base class, we don't currently use its inf to specialize the class. ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T, U]: def __new__(cls, *args, **kwargs) -> "C[T, U]": @@ -412,7 +412,7 @@ class D[V](C[V, int]): # revealed: ty_extensions.GenericContext[V@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[V@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(1)) # revealed: D[Literal[1]] ``` @@ -420,7 +420,7 @@ reveal_type(D(1)) # revealed: D[Literal[1]] ### Generic class inherits `__init__` from generic base class ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T, U]: def __init__(self, t: T, u: U) -> None: ... @@ -431,7 +431,7 @@ class D[T, U](C[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(C(1, "str")) # revealed: C[Literal[1], Literal["str"]] reveal_type(D(1, "str")) # revealed: D[Literal[1], Literal["str"]] @@ -442,7 +442,7 @@ reveal_type(D(1, "str")) # revealed: D[Literal[1], Literal["str"]] This is a specific example of the above, since it was reported specifically by a user. ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class D[T, U](dict[T, U]): pass @@ -450,7 +450,7 @@ class D[T, U](dict[T, U]): # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D(key=1)) # revealed: D[str, int] ``` @@ -462,14 +462,14 @@ for `tuple`, so we use a different mechanism to make sure it has the right inher context. But from the user's point of view, this is another example of the above.) ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T, U](tuple[T, U]): ... # revealed: ty_extensions.GenericContext[T@C, U@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, U@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C((1, 2))) # revealed: C[Literal[1], Literal[2]] ``` @@ -494,7 +494,7 @@ def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: t ### `__init__` is itself generic ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: x: T @@ -504,7 +504,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, S@__init__] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C(1, 1)) # revealed: C[int] reveal_type(C(1, "string")) # revealed: C[int] @@ -519,7 +519,7 @@ wrong_innards: C[int] = C("five", 1) ```py from __future__ import annotations from typing import overload -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T]: # we need to use the type variable or else the class is bivariant in T, and @@ -539,7 +539,7 @@ class C[T]: # revealed: ty_extensions.GenericContext[T@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C("string")) # revealed: C[str] reveal_type(C(b"bytes")) # revealed: C[bytes] @@ -575,7 +575,7 @@ class D[T, U]: # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D("string")) # revealed: D[str, Literal["string"]] reveal_type(D(1)) # revealed: D[str, Literal[1]] @@ -586,7 +586,7 @@ reveal_type(D(1, "string")) # revealed: D[int, Literal["string"]] ```py from dataclasses import dataclass -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable @dataclass class A[T]: @@ -595,7 +595,7 @@ class A[T]: # revealed: ty_extensions.GenericContext[T@A] reveal_type(generic_context(A)) # revealed: ty_extensions.GenericContext[T@A] -reveal_type(generic_context(into_callable(A))) +reveal_type(generic_context(into_regular_callable(A))) reveal_type(A(x=1)) # revealed: A[int] ``` @@ -603,14 +603,14 @@ reveal_type(A(x=1)) # revealed: A[int] ### Class typevar has another typevar as a default ```py -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable class C[T, U = T]: ... # revealed: ty_extensions.GenericContext[T@C, U@C] reveal_type(generic_context(C)) # revealed: ty_extensions.GenericContext[T@C, U@C] -reveal_type(generic_context(into_callable(C))) +reveal_type(generic_context(into_regular_callable(C))) reveal_type(C()) # revealed: C[Unknown, Unknown] @@ -620,7 +620,7 @@ class D[T, U = T]: # revealed: ty_extensions.GenericContext[T@D, U@D] reveal_type(generic_context(D)) # revealed: ty_extensions.GenericContext[T@D, U@D] -reveal_type(generic_context(into_callable(D))) +reveal_type(generic_context(into_regular_callable(D))) reveal_type(D()) # revealed: D[Unknown, Unknown] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index 1a624459a4de0..956105c723d35 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -792,7 +792,7 @@ specializations of a generic function. ```py from typing import Any, Callable, NoReturn, overload, Self -from ty_extensions import generic_context, into_callable +from ty_extensions import generic_context, into_regular_callable def accepts_callable[**P, R](callable: Callable[P, R]) -> Callable[P, R]: return callable @@ -801,7 +801,7 @@ def returns_int() -> int: raise NotImplementedError # revealed: () -> int -reveal_type(into_callable(returns_int)) +reveal_type(into_regular_callable(returns_int)) # revealed: () -> int reveal_type(accepts_callable(returns_int)) # revealed: int @@ -810,7 +810,7 @@ reveal_type(accepts_callable(returns_int)()) class ClassWithoutConstructor: ... # revealed: () -> ClassWithoutConstructor -reveal_type(into_callable(ClassWithoutConstructor)) +reveal_type(into_regular_callable(ClassWithoutConstructor)) # revealed: () -> ClassWithoutConstructor reveal_type(accepts_callable(ClassWithoutConstructor)) # revealed: ClassWithoutConstructor @@ -821,7 +821,7 @@ class ClassWithNew: raise NotImplementedError # revealed: (...) -> ClassWithNew -reveal_type(into_callable(ClassWithNew)) +reveal_type(into_regular_callable(ClassWithNew)) # revealed: (...) -> ClassWithNew reveal_type(accepts_callable(ClassWithNew)) # revealed: ClassWithNew @@ -831,7 +831,7 @@ class ClassWithInit: def __init__(self) -> None: ... # revealed: () -> ClassWithInit -reveal_type(into_callable(ClassWithInit)) +reveal_type(into_regular_callable(ClassWithInit)) # revealed: () -> ClassWithInit reveal_type(accepts_callable(ClassWithInit)) # revealed: ClassWithInit @@ -845,7 +845,7 @@ class ClassWithNewAndInit: # TODO: We do not currently solve a common behavioral supertype for the two solutions of P. # revealed: ((...) -> ClassWithNewAndInit) | ((x: int) -> ClassWithNewAndInit) -reveal_type(into_callable(ClassWithNewAndInit)) +reveal_type(into_regular_callable(ClassWithNewAndInit)) # TODO: revealed: ((...) -> ClassWithNewAndInit) | ((x: int) -> ClassWithNewAndInit) # revealed: (...) -> ClassWithNewAndInit reveal_type(accepts_callable(ClassWithNewAndInit)) @@ -863,7 +863,7 @@ class ClassWithNoReturnMetatype(metaclass=Meta): # TODO: The return types here are wrong, because we end up creating a constraint (Never ≤ R), which # we confuse with "R has no lower bound". # revealed: (...) -> Never -reveal_type(into_callable(ClassWithNoReturnMetatype)) +reveal_type(into_regular_callable(ClassWithNoReturnMetatype)) # TODO: revealed: (...) -> Never # revealed: (...) -> Unknown reveal_type(accepts_callable(ClassWithNoReturnMetatype)) @@ -880,7 +880,7 @@ class ClassWithIgnoredInit: def __init__(self, x: int) -> None: ... # revealed: () -> Proxy -reveal_type(into_callable(ClassWithIgnoredInit)) +reveal_type(into_regular_callable(ClassWithIgnoredInit)) # revealed: () -> Proxy reveal_type(accepts_callable(ClassWithIgnoredInit)) # revealed: Proxy @@ -902,7 +902,7 @@ class ClassWithOverloadedInit[T]: # solution. # revealed: Overload[[T](x: int) -> ClassWithOverloadedInit[int], [T](x: str) -> ClassWithOverloadedInit[str]] -reveal_type(into_callable(ClassWithOverloadedInit)) +reveal_type(into_regular_callable(ClassWithOverloadedInit)) # TODO: revealed: Overload[(x: int) -> ClassWithOverloadedInit[int], (x: str) -> ClassWithOverloadedInit[str]] # revealed: Overload[[T](x: int) -> ClassWithOverloadedInit[int] | ClassWithOverloadedInit[str], [T](x: str) -> ClassWithOverloadedInit[int] | ClassWithOverloadedInit[str]] reveal_type(accepts_callable(ClassWithOverloadedInit)) @@ -921,9 +921,9 @@ class GenericClass[T]: def _(x: list[str]): # revealed: [T](x: list[T], y: list[T]) -> GenericClass[T] - reveal_type(into_callable(GenericClass)) + reveal_type(into_regular_callable(GenericClass)) # revealed: ty_extensions.GenericContext[T@GenericClass] - reveal_type(generic_context(into_callable(GenericClass))) + reveal_type(generic_context(into_regular_callable(GenericClass))) # revealed: [T](x: list[T], y: list[T]) -> GenericClass[T] reveal_type(accepts_callable(GenericClass)) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md index 16acbce4ded4c..49aef5882e9d9 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/scoping.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/scoping.md @@ -546,7 +546,7 @@ parameters from the enclosing class. ```py from typing import Generic, TypeVar -from ty_extensions import into_callable +from ty_extensions import into_regular_callable T = TypeVar("T") S = TypeVar("S") @@ -557,7 +557,7 @@ class Foo(Generic[T]): def f(x: type[Foo[T]]) -> T: # revealed: [S](self, x: T@f, y: S) -> tuple[T@f, S] - reveal_type(into_callable(x.bar)) + reveal_type(into_regular_callable(x.bar)) raise NotImplementedError ``` diff --git a/crates/ty_python_semantic/resources/mdtest/regression/3020_callable_type_of.md b/crates/ty_python_semantic/resources/mdtest/regression/3020_callable_type_of.md new file mode 100644 index 0000000000000..36a562598bed9 --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/regression/3020_callable_type_of.md @@ -0,0 +1,22 @@ +# Regression test for #3020 + +Regression test for [this issue](https://github.com/astral-sh/ty/issues/3020). + +```python +from typing import Callable +from ty_extensions import ( + RegularCallableTypeOf, + TypeOf, + into_regular_callable, + static_assert, + is_assignable_to, + is_subtype_of, +) + +def f(a: int, b: str, /) -> None: ... + +static_assert(is_assignable_to(Callable[[int, str], None], RegularCallableTypeOf[f])) +static_assert(is_subtype_of(Callable[[int, str], None], RegularCallableTypeOf[f])) +static_assert(is_assignable_to(Callable[[int, str], None], TypeOf[into_regular_callable(f)])) +static_assert(is_subtype_of(Callable[[int, str], None], TypeOf[into_regular_callable(f)])) +``` diff --git a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md index e20a82ca7b628..64a9d9fafbabd 100644 --- a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md +++ b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md @@ -471,9 +471,13 @@ reveal_type(foo) # revealed: def foo() -> def foo() -> def foo() -> def foo() - ## `CallableTypeOf` -The `CallableTypeOf` special form can be used to extract the `Callable` structural type inhabited by -a given callable object. This can be used to get the externally visibly signature of the object, -which can then be used to test various type properties. +The `CallableTypeOf` special form can be used to extract the callable type inhabited by a given +callable object. This can be used to get the externally visible signature of the object, which can +then be used to test various type properties. + +Unlike a plain `typing.Callable[...]`, `CallableTypeOf[...]` preserves function-like behavior. This +means method-like and descriptor-like callables remain distinct from regular callables in some +type-theoretic checks. It accepts a single type parameter which is expected to be a callable object. @@ -546,3 +550,23 @@ def _( reveal_type(c7) # revealed: (x: int) -> Foo reveal_type(c8) # revealed: (x: int) -> str ``` + +## `RegularCallableTypeOf` + +The `RegularCallableTypeOf` special form also extracts a callable type from a callable object, but +it normalizes the result to a regular `typing.Callable`-style type. + +This keeps the callable signatures while discarding function-like behavior. Use it when you want to +compare a callable against ordinary `Callable[...]` types without preserving descriptor semantics. + +It accepts a single type parameter which is expected to be a callable object. + +```py +from typing import Callable +from ty_extensions import CallableTypeOf, RegularCallableTypeOf, is_assignable_to, static_assert + +def f(x: int, /) -> None: ... + +static_assert(not is_assignable_to(Callable[[int], None], CallableTypeOf[f])) +static_assert(is_assignable_to(Callable[[int], None], RegularCallableTypeOf[f])) +``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_display/callable.md b/crates/ty_python_semantic/resources/mdtest/type_display/callable.md index d90911213d603..aa2beb25a446e 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_display/callable.md +++ b/crates/ty_python_semantic/resources/mdtest/type_display/callable.md @@ -20,7 +20,7 @@ We don't parenthesize display of an overloaded callable, since it is already wra ```py from typing import overload, Callable -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf @overload def f(x: int) -> bool: ... @@ -29,7 +29,7 @@ def f(x: str) -> str: ... def f(x: int | str) -> bool | str: return bool(x) if isinstance(x, int) else str(x) -def _(flag: bool, c: CallableTypeOf[f]): +def _(flag: bool, c: RegularCallableTypeOf[f]): x = c if flag else True reveal_type(x) # revealed: Overload[(x: int) -> bool, (x: str) -> str] | Literal[True] ``` diff --git a/crates/ty_python_semantic/resources/mdtest/type_of/generics.md b/crates/ty_python_semantic/resources/mdtest/type_of/generics.md index 24ea3ea6a6616..04d9cb70ca0b9 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_of/generics.md +++ b/crates/ty_python_semantic/resources/mdtest/type_of/generics.md @@ -516,7 +516,7 @@ expects_type_c_default_of_int_str(C[str, int]) the class that the instance-type refers to. ```py -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf class TakesStrInConstructor: def __init__(self, x: int, y: str | None = None): ... @@ -581,15 +581,15 @@ def f[ reveal_type(type_t_constrained(42, 42)) # revealed: T4@f def g( - object_class_upcast: CallableTypeOf[object], - bare_type_upcast: CallableTypeOf[bare_type], - type_object_upcast: CallableTypeOf[type_object], - type_t_unbound_upcast: CallableTypeOf[type_t_unbound], - type_t_object_bound_upcast: CallableTypeOf[type_t_object_bound], - type_int_upcast: CallableTypeOf[type_int], - type_t_int_bound_upcast: CallableTypeOf[type_t_int_bound], - type_t_union_bound_upcast: CallableTypeOf[type_t_union_bound], - type_t_constrained_upcast: CallableTypeOf[type_t_constrained], + object_class_upcast: RegularCallableTypeOf[object], + bare_type_upcast: RegularCallableTypeOf[bare_type], + type_object_upcast: RegularCallableTypeOf[type_object], + type_t_unbound_upcast: RegularCallableTypeOf[type_t_unbound], + type_t_object_bound_upcast: RegularCallableTypeOf[type_t_object_bound], + type_int_upcast: RegularCallableTypeOf[type_int], + type_t_int_bound_upcast: RegularCallableTypeOf[type_t_int_bound], + type_t_union_bound_upcast: RegularCallableTypeOf[type_t_union_bound], + type_t_constrained_upcast: RegularCallableTypeOf[type_t_constrained], ): reveal_type(object_class_upcast) # revealed: () -> object diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/implies_subtype_of.md b/crates/ty_python_semantic/resources/mdtest/type_properties/implies_subtype_of.md index 1bfcedb9068a4..fd23c826e4b04 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/implies_subtype_of.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/implies_subtype_of.md @@ -449,7 +449,7 @@ the generic callable.) ```py from typing import Callable -from ty_extensions import CallableTypeOf, ConstraintSet, TypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, ConstraintSet, TypeOf, is_subtype_of, static_assert def identity[T](t: T) -> T: return t @@ -462,9 +462,9 @@ static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[int], i static_assert(constraints.implies_subtype_of(TypeOf[identity], Callable[[str], str])) static_assert(not constraints.implies_subtype_of(TypeOf[identity], Callable[[str], int])) -static_assert(constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[int], int])) -static_assert(constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[str], str])) -static_assert(not constraints.implies_subtype_of(CallableTypeOf[identity], Callable[[str], int])) +static_assert(constraints.implies_subtype_of(RegularCallableTypeOf[identity], Callable[[int], int])) +static_assert(constraints.implies_subtype_of(RegularCallableTypeOf[identity], Callable[[str], str])) +static_assert(not constraints.implies_subtype_of(RegularCallableTypeOf[identity], Callable[[str], int])) static_assert(constraints.implies_subtype_of(TypeOf[identity], GenericIdentity[int])) static_assert(constraints.implies_subtype_of(TypeOf[identity], GenericIdentity[str])) @@ -481,9 +481,9 @@ static_assert(not constraints.implies_subtype_of(Callable[[int], int], TypeOf[id static_assert(not constraints.implies_subtype_of(Callable[[str], str], TypeOf[identity])) static_assert(not constraints.implies_subtype_of(Callable[[str], int], TypeOf[identity])) -static_assert(not constraints.implies_subtype_of(Callable[[int], int], CallableTypeOf[identity])) -static_assert(not constraints.implies_subtype_of(Callable[[str], str], CallableTypeOf[identity])) -static_assert(not constraints.implies_subtype_of(Callable[[str], int], CallableTypeOf[identity])) +static_assert(not constraints.implies_subtype_of(Callable[[int], int], RegularCallableTypeOf[identity])) +static_assert(not constraints.implies_subtype_of(Callable[[str], str], RegularCallableTypeOf[identity])) +static_assert(not constraints.implies_subtype_of(Callable[[str], int], RegularCallableTypeOf[identity])) static_assert(not constraints.implies_subtype_of(GenericIdentity[int], TypeOf[identity])) static_assert(not constraints.implies_subtype_of(GenericIdentity[str], TypeOf[identity])) diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md index cb3755be1cde4..1fdecd5a72b46 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_assignable_to.md @@ -769,7 +769,7 @@ The root cause was that we failed to properly materialize a `Callable[..., Unkno `Unknown` return type originated from a missing annotation. ```py -from ty_extensions import static_assert, is_assignable_to, Intersection, Not, Unknown, CallableTypeOf +from ty_extensions import static_assert, is_assignable_to, Intersection, Not, Unknown, RegularCallableTypeOf from typing import Callable # `Callable[..., Unknown]` has explicit Unknown return type @@ -778,8 +778,8 @@ static_assert(is_assignable_to(Intersection[Not[type], Not[Callable[..., Unknown # Function with no return annotation (has implicit Unknown return type internally) def no_return_annotation(*args, **kwargs): ... -# `CallableTypeOf[no_return_annotation]` has `returns: None` internally (no annotation) -static_assert(is_assignable_to(Intersection[Not[type], Not[CallableTypeOf[no_return_annotation]]], Not[type])) +# `RegularCallableTypeOf[no_return_annotation]` has `returns: None` internally (no annotation) +static_assert(is_assignable_to(Intersection[Not[type], Not[RegularCallableTypeOf[no_return_annotation]]], Not[type])) ``` ## Intersections with non-fully-static negated elements @@ -933,7 +933,7 @@ are covered in the [subtyping tests](./is_subtype_of.md#callable). ### Return type ```py -from ty_extensions import CallableTypeOf, Unknown, static_assert, is_assignable_to +from ty_extensions import RegularCallableTypeOf, Unknown, static_assert, is_assignable_to from typing import Any, Callable static_assert(is_assignable_to(Callable[[], Any], Callable[[], int])) @@ -963,7 +963,7 @@ A `Callable` which uses the gradual form (`...`) for the parameter types is cons input signature. ```py -from ty_extensions import CallableTypeOf, static_assert, is_assignable_to +from ty_extensions import RegularCallableTypeOf, static_assert, is_assignable_to from typing import Any, Callable static_assert(is_assignable_to(Callable[[], None], Callable[..., None])) @@ -981,12 +981,12 @@ def keyword_only(*, a: int, b: int) -> None: ... def keyword_variadic(**kwargs: int) -> None: ... def mixed(a: int, /, b: int, *args: int, c: int, **kwargs: int) -> None: ... -static_assert(is_assignable_to(CallableTypeOf[positional_only], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[positional_or_keyword], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[variadic], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[keyword_only], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[keyword_variadic], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[mixed], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[positional_only], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[positional_or_keyword], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[variadic], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[keyword_only], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[keyword_variadic], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[mixed], Callable[..., None])) ``` And, even if the parameters are unannotated. @@ -999,12 +999,12 @@ def keyword_only(*, a, b) -> None: ... def keyword_variadic(**kwargs) -> None: ... def mixed(a, /, b, *args, c, **kwargs) -> None: ... -static_assert(is_assignable_to(CallableTypeOf[positional_only], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[positional_or_keyword], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[variadic], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[keyword_only], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[keyword_variadic], Callable[..., None])) -static_assert(is_assignable_to(CallableTypeOf[mixed], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[positional_only], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[positional_or_keyword], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[variadic], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[keyword_only], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[keyword_variadic], Callable[..., None])) +static_assert(is_assignable_to(RegularCallableTypeOf[mixed], Callable[..., None])) ``` ### Function types @@ -1297,7 +1297,7 @@ the generic callable.) ```py from typing import Callable -from ty_extensions import CallableTypeOf, TypeOf, is_assignable_to, static_assert +from ty_extensions import RegularCallableTypeOf, TypeOf, is_assignable_to, static_assert def identity[T](t: T) -> T: return t @@ -1308,11 +1308,11 @@ static_assert(is_assignable_to(TypeOf[identity], Callable[[str], str])) # error: [static-assert-error] static_assert(not is_assignable_to(TypeOf[identity], Callable[[str], int])) -static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[int], int])) -static_assert(is_assignable_to(CallableTypeOf[identity], Callable[[str], str])) +static_assert(is_assignable_to(RegularCallableTypeOf[identity], Callable[[int], int])) +static_assert(is_assignable_to(RegularCallableTypeOf[identity], Callable[[str], str])) # TODO: no error # error: [static-assert-error] -static_assert(not is_assignable_to(CallableTypeOf[identity], Callable[[str], int])) +static_assert(not is_assignable_to(RegularCallableTypeOf[identity], Callable[[str], int])) ``` The reverse is not true — if someone expects a generic function that can be called with any @@ -1323,9 +1323,9 @@ static_assert(not is_assignable_to(Callable[[int], int], TypeOf[identity])) static_assert(not is_assignable_to(Callable[[str], str], TypeOf[identity])) static_assert(not is_assignable_to(Callable[[str], int], TypeOf[identity])) -static_assert(not is_assignable_to(Callable[[int], int], CallableTypeOf[identity])) -static_assert(not is_assignable_to(Callable[[str], str], CallableTypeOf[identity])) -static_assert(not is_assignable_to(Callable[[str], int], CallableTypeOf[identity])) +static_assert(not is_assignable_to(Callable[[int], int], RegularCallableTypeOf[identity])) +static_assert(not is_assignable_to(Callable[[str], str], RegularCallableTypeOf[identity])) +static_assert(not is_assignable_to(Callable[[str], int], RegularCallableTypeOf[identity])) ``` ## Generics diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md index 0b2d95842f2a8..1e9ffc79efa4c 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_disjoint_from.md @@ -715,13 +715,13 @@ As such, for any two callable types, it is possible to conceive of a runtime cal would inhabit both types simultaneously. ```py -from ty_extensions import CallableTypeOf, is_disjoint_from, static_assert +from ty_extensions import RegularCallableTypeOf, is_disjoint_from, static_assert from typing_extensions import Callable, Literal, Never def mixed(a: int, /, b: str, *args: int, c: int = 2, **kwargs: int) -> None: ... -static_assert(not is_disjoint_from(Callable[[], Never], CallableTypeOf[mixed])) -static_assert(not is_disjoint_from(Callable[[int, str], float], CallableTypeOf[mixed])) +static_assert(not is_disjoint_from(Callable[[], Never], RegularCallableTypeOf[mixed])) +static_assert(not is_disjoint_from(Callable[[int, str], float], RegularCallableTypeOf[mixed])) # Using gradual form static_assert(not is_disjoint_from(Callable[..., None], Callable[[], None])) @@ -736,7 +736,7 @@ static_assert(not is_disjoint_from(Callable[[Never], str], Callable[[Never], int A callable type is disjoint from all literal types. ```py -from ty_extensions import CallableTypeOf, is_disjoint_from, static_assert +from ty_extensions import RegularCallableTypeOf, is_disjoint_from, static_assert from typing_extensions import Callable, Literal, Never static_assert(is_disjoint_from(Callable[[], None], Literal[""])) @@ -749,7 +749,7 @@ A callable type is disjoint from nominal instance types where the classes are fi `__call__` is not callable. ```py -from ty_extensions import CallableTypeOf, is_disjoint_from, static_assert +from ty_extensions import RegularCallableTypeOf, is_disjoint_from, static_assert from typing_extensions import Any, Callable, final @final diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md index ffad25785e63d..2723eb6ff6807 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_equivalent_to.md @@ -235,14 +235,18 @@ the parameter in one of the callable has a default value then the corresponding other callable should also have a default value. ```py -from ty_extensions import CallableTypeOf, is_equivalent_to, static_assert +from ty_extensions import RegularCallableTypeOf, is_equivalent_to, static_assert from typing import Callable def f1(a: int = 1) -> None: ... def f2(a: int = 2) -> None: ... -static_assert(is_equivalent_to(CallableTypeOf[f1], CallableTypeOf[f2])) -static_assert(is_equivalent_to(CallableTypeOf[f1] | bool | CallableTypeOf[f2], CallableTypeOf[f2] | bool | CallableTypeOf[f1])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f1], RegularCallableTypeOf[f2])) +static_assert( + is_equivalent_to( + RegularCallableTypeOf[f1] | bool | RegularCallableTypeOf[f2], RegularCallableTypeOf[f2] | bool | RegularCallableTypeOf[f1] + ) +) ``` The names of the positional-only, variadic and keyword-variadic parameters does not need to be the @@ -252,8 +256,12 @@ same. def f3(a1: int, /, *args1: int, **kwargs2: int) -> None: ... def f4(a2: int, /, *args2: int, **kwargs1: int) -> None: ... -static_assert(is_equivalent_to(CallableTypeOf[f3], CallableTypeOf[f4])) -static_assert(is_equivalent_to(CallableTypeOf[f3] | bool | CallableTypeOf[f4], CallableTypeOf[f4] | bool | CallableTypeOf[f3])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f3], RegularCallableTypeOf[f4])) +static_assert( + is_equivalent_to( + RegularCallableTypeOf[f3] | bool | RegularCallableTypeOf[f4], RegularCallableTypeOf[f4] | bool | RegularCallableTypeOf[f3] + ) +) ``` Putting it all together, the following two callables are equivalent: @@ -262,8 +270,12 @@ Putting it all together, the following two callables are equivalent: def f5(a1: int, /, b: float, c: bool = False, *args1: int, d: int = 1, e: str, **kwargs1: float) -> None: ... def f6(a2: int, /, b: float, c: bool = True, *args2: int, d: int = 2, e: str, **kwargs2: float) -> None: ... -static_assert(is_equivalent_to(CallableTypeOf[f5], CallableTypeOf[f6])) -static_assert(is_equivalent_to(CallableTypeOf[f5] | bool | CallableTypeOf[f6], CallableTypeOf[f6] | bool | CallableTypeOf[f5])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f5], RegularCallableTypeOf[f6])) +static_assert( + is_equivalent_to( + RegularCallableTypeOf[f5] | bool | RegularCallableTypeOf[f6], RegularCallableTypeOf[f6] | bool | RegularCallableTypeOf[f5] + ) +) ``` ### Not equivalent @@ -271,7 +283,7 @@ static_assert(is_equivalent_to(CallableTypeOf[f5] | bool | CallableTypeOf[f6], C There are multiple cases when two callable types are not equivalent which are enumerated below. ```py -from ty_extensions import CallableTypeOf, is_equivalent_to, static_assert +from ty_extensions import RegularCallableTypeOf, is_equivalent_to, static_assert from typing import Callable ``` @@ -281,7 +293,7 @@ When the number of parameters is different: def f1(a: int) -> None: ... def f2(a: int, b: int) -> None: ... -static_assert(not is_equivalent_to(CallableTypeOf[f1], CallableTypeOf[f2])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f1], RegularCallableTypeOf[f2])) ``` When the return types are not equivalent in one or both of the callable types: @@ -291,9 +303,9 @@ def f3(): ... def f4() -> None: ... static_assert(not is_equivalent_to(Callable[[], int], Callable[[], None])) -static_assert(is_equivalent_to(CallableTypeOf[f3], CallableTypeOf[f3])) -static_assert(not is_equivalent_to(CallableTypeOf[f3], CallableTypeOf[f4])) -static_assert(not is_equivalent_to(CallableTypeOf[f4], CallableTypeOf[f3])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f3], RegularCallableTypeOf[f3])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f3], RegularCallableTypeOf[f4])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f4], RegularCallableTypeOf[f3])) ``` When the parameter names are different: @@ -302,13 +314,13 @@ When the parameter names are different: def f5(a: int) -> None: ... def f6(b: int) -> None: ... -static_assert(not is_equivalent_to(CallableTypeOf[f5], CallableTypeOf[f6])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f5], RegularCallableTypeOf[f6])) ``` When only one of the callable types has parameter names: ```py -static_assert(not is_equivalent_to(CallableTypeOf[f5], Callable[[int], None])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f5], Callable[[int], None])) ``` When the parameter kinds are different: @@ -317,7 +329,7 @@ When the parameter kinds are different: def f7(a: int, /) -> None: ... def f8(a: int) -> None: ... -static_assert(not is_equivalent_to(CallableTypeOf[f7], CallableTypeOf[f8])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f7], RegularCallableTypeOf[f8])) ``` When the annotated types of the parameters are not equivalent or absent in one or both of the @@ -328,10 +340,10 @@ def f9(a: int) -> None: ... def f10(a: str) -> None: ... def f11(a) -> None: ... -static_assert(not is_equivalent_to(CallableTypeOf[f9], CallableTypeOf[f10])) -static_assert(not is_equivalent_to(CallableTypeOf[f10], CallableTypeOf[f11])) -static_assert(not is_equivalent_to(CallableTypeOf[f11], CallableTypeOf[f10])) -static_assert(is_equivalent_to(CallableTypeOf[f11], CallableTypeOf[f11])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f9], RegularCallableTypeOf[f10])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f10], RegularCallableTypeOf[f11])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f11], RegularCallableTypeOf[f10])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f11], RegularCallableTypeOf[f11])) ``` When the default value for a parameter is present only in one of the callable type: @@ -340,8 +352,8 @@ When the default value for a parameter is present only in one of the callable ty def f12(a: int) -> None: ... def f13(a: int = 2) -> None: ... -static_assert(not is_equivalent_to(CallableTypeOf[f12], CallableTypeOf[f13])) -static_assert(not is_equivalent_to(CallableTypeOf[f13], CallableTypeOf[f12])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f12], RegularCallableTypeOf[f13])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f13], RegularCallableTypeOf[f12])) ``` ### Unions containing `Callable`s @@ -350,12 +362,12 @@ Two unions containing different `Callable` types are equivalent even if the unio ordered: ```py -from ty_extensions import CallableTypeOf, Unknown, is_equivalent_to, static_assert +from ty_extensions import RegularCallableTypeOf, Unknown, is_equivalent_to, static_assert def f(x): ... def g(x: Unknown): ... -static_assert(is_equivalent_to(CallableTypeOf[f] | int | str, str | int | CallableTypeOf[g])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f] | int | str, str | int | RegularCallableTypeOf[g])) ``` ### Unions containing `Callable`s containing unions @@ -391,13 +403,13 @@ def overloaded(a: Grandparent) -> None: ... ``` ```py -from ty_extensions import CallableTypeOf, is_equivalent_to, static_assert +from ty_extensions import RegularCallableTypeOf, is_equivalent_to, static_assert from overloaded import Grandparent, Parent, Child, overloaded def grandparent(a: Grandparent) -> None: ... -static_assert(is_equivalent_to(CallableTypeOf[grandparent], CallableTypeOf[overloaded])) -static_assert(is_equivalent_to(CallableTypeOf[overloaded], CallableTypeOf[grandparent])) +static_assert(is_equivalent_to(RegularCallableTypeOf[grandparent], RegularCallableTypeOf[overloaded])) +static_assert(is_equivalent_to(RegularCallableTypeOf[overloaded], RegularCallableTypeOf[grandparent])) ``` #### Both overloads @@ -424,11 +436,11 @@ def cpg(a: Grandparent) -> None: ... ``` ```py -from ty_extensions import CallableTypeOf, is_equivalent_to, static_assert +from ty_extensions import RegularCallableTypeOf, is_equivalent_to, static_assert from overloaded import pg, cpg -static_assert(is_equivalent_to(CallableTypeOf[pg], CallableTypeOf[cpg])) -static_assert(is_equivalent_to(CallableTypeOf[cpg], CallableTypeOf[pg])) +static_assert(is_equivalent_to(RegularCallableTypeOf[pg], RegularCallableTypeOf[cpg])) +static_assert(is_equivalent_to(RegularCallableTypeOf[cpg], RegularCallableTypeOf[pg])) ``` ### Function-literal types and bound-method types @@ -463,7 +475,7 @@ gradual types. The cases with fully static types and using different combination are covered above. ```py -from ty_extensions import Unknown, CallableTypeOf, TypeOf, is_equivalent_to, static_assert +from ty_extensions import Unknown, CallableTypeOf, RegularCallableTypeOf, TypeOf, is_equivalent_to, static_assert from typing import Any, Callable static_assert(is_equivalent_to(Callable[..., int], Callable[..., int])) @@ -485,7 +497,7 @@ def f1(): def f1_equivalent() -> Any: return -static_assert(is_equivalent_to(CallableTypeOf[f1], CallableTypeOf[f1_equivalent])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f1], RegularCallableTypeOf[f1_equivalent])) ``` And, similarly for parameters with no annotations. @@ -497,7 +509,7 @@ def f2(a, b, /) -> None: def f2_equivalent(a: Any, b: Any, /) -> None: return -static_assert(is_equivalent_to(CallableTypeOf[f2], CallableTypeOf[f2_equivalent])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f2], RegularCallableTypeOf[f2_equivalent])) ``` A function definition that includes both `*args` and `**kwargs` parameter that are annotated as @@ -541,16 +553,16 @@ def variadic_kwargs(**kwargs): return def _( - signature_variadic_args: CallableTypeOf[variadic_args], - signature_variadic_kwargs: CallableTypeOf[variadic_kwargs], + signature_variadic_args: RegularCallableTypeOf[variadic_args], + signature_variadic_kwargs: RegularCallableTypeOf[variadic_kwargs], ) -> None: # revealed: (*args) -> Unknown reveal_type(signature_variadic_args) # revealed: (**kwargs) -> Unknown reveal_type(signature_variadic_kwargs) -static_assert(not is_equivalent_to(CallableTypeOf[variadic_args], Callable[..., Any])) -static_assert(not is_equivalent_to(CallableTypeOf[variadic_kwargs], Callable[..., Any])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[variadic_args], Callable[..., Any])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[variadic_kwargs], Callable[..., Any])) ``` Parameter names, default values, and it's kind should also be considered when checking for gradual @@ -560,19 +572,23 @@ equivalence. def f1(a): ... def f2(b): ... -static_assert(not is_equivalent_to(CallableTypeOf[f1], CallableTypeOf[f2])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f1], RegularCallableTypeOf[f2])) def f3(a=1): ... def f4(a=2): ... def f5(a): ... -static_assert(is_equivalent_to(CallableTypeOf[f3], CallableTypeOf[f4])) -static_assert(is_equivalent_to(CallableTypeOf[f3] | bool | CallableTypeOf[f4], CallableTypeOf[f4] | bool | CallableTypeOf[f3])) -static_assert(not is_equivalent_to(CallableTypeOf[f3], CallableTypeOf[f5])) +static_assert(is_equivalent_to(RegularCallableTypeOf[f3], RegularCallableTypeOf[f4])) +static_assert( + is_equivalent_to( + RegularCallableTypeOf[f3] | bool | RegularCallableTypeOf[f4], RegularCallableTypeOf[f4] | bool | RegularCallableTypeOf[f3] + ) +) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f3], RegularCallableTypeOf[f5])) def f6(a, /): ... -static_assert(not is_equivalent_to(CallableTypeOf[f1], CallableTypeOf[f6])) +static_assert(not is_equivalent_to(RegularCallableTypeOf[f1], RegularCallableTypeOf[f6])) ``` ## Module-literal types diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md index 5c56191a9f22a..e487bfce3ae97 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md @@ -1002,13 +1002,13 @@ Parameter types are contravariant. ```py from typing import Callable -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert, TypeOf +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert, TypeOf def float_param(a: float, /) -> None: ... def int_param(a: int, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[float_param], CallableTypeOf[int_param])) -static_assert(not is_subtype_of(CallableTypeOf[int_param], CallableTypeOf[float_param])) +static_assert(is_subtype_of(RegularCallableTypeOf[float_param], RegularCallableTypeOf[int_param])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_param], RegularCallableTypeOf[float_param])) static_assert(is_subtype_of(TypeOf[int_param], Callable[[int], None])) static_assert(is_subtype_of(TypeOf[float_param], Callable[[float], None])) @@ -1022,8 +1022,8 @@ Parameter name is not required to be the same for positional-only parameters at ```py def int_param_different_name(b: int, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[int_param], CallableTypeOf[int_param_different_name])) -static_assert(is_subtype_of(CallableTypeOf[int_param_different_name], CallableTypeOf[int_param])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_param], RegularCallableTypeOf[int_param_different_name])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_param_different_name], RegularCallableTypeOf[int_param])) ``` Multiple positional-only parameters are checked in order: @@ -1032,8 +1032,8 @@ Multiple positional-only parameters are checked in order: def multi_param1(a: float, b: int, c: str, /) -> None: ... def multi_param2(b: int, c: bool, a: str, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_param1], CallableTypeOf[multi_param2])) -static_assert(not is_subtype_of(CallableTypeOf[multi_param2], CallableTypeOf[multi_param1])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_param1], RegularCallableTypeOf[multi_param2])) +static_assert(not is_subtype_of(RegularCallableTypeOf[multi_param2], RegularCallableTypeOf[multi_param1])) static_assert(is_subtype_of(TypeOf[multi_param1], Callable[[float, int, str], None])) @@ -1047,17 +1047,17 @@ corresponding position in the supertype does not need to have a default value. ```py from typing import Callable -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert, TypeOf +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert, TypeOf def float_with_default(a: float = 1, /) -> None: ... def int_with_default(a: int = 1, /) -> None: ... def int_without_default(a: int, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[float_with_default], CallableTypeOf[int_with_default])) -static_assert(not is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[float_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[float_with_default], RegularCallableTypeOf[int_with_default])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[float_with_default])) -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[int_without_default])) -static_assert(not is_subtype_of(CallableTypeOf[int_without_default], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[int_without_default])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_without_default], RegularCallableTypeOf[int_with_default])) static_assert(is_subtype_of(TypeOf[int_with_default], Callable[[int], None])) static_assert(is_subtype_of(TypeOf[int_with_default], Callable[[], None])) @@ -1072,9 +1072,9 @@ As the parameter itself is optional, it can be omitted in the supertype: ```py def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[int_without_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_without_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[int_with_default])) ``` The subtype can include any number of positional-only parameters as long as they have the default @@ -1083,8 +1083,8 @@ value: ```py def multi_param(a: float = 1, b: int = 2, c: str = "3", /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_param], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[multi_param])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_param], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[multi_param])) ``` #### Positional-only with other kinds @@ -1093,7 +1093,7 @@ If a parameter is declared as positional-only, then the corresponding parameter cannot be any other parameter kind. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def positional_only(a: int, /) -> None: ... def standard(a: int) -> None: ... @@ -1101,10 +1101,10 @@ def keyword_only(*, a: int) -> None: ... def variadic(*a: int) -> None: ... def keyword_variadic(**a: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[positional_only], CallableTypeOf[standard])) -static_assert(not is_subtype_of(CallableTypeOf[positional_only], CallableTypeOf[keyword_only])) -static_assert(not is_subtype_of(CallableTypeOf[positional_only], CallableTypeOf[variadic])) -static_assert(not is_subtype_of(CallableTypeOf[positional_only], CallableTypeOf[keyword_variadic])) +static_assert(not is_subtype_of(RegularCallableTypeOf[positional_only], RegularCallableTypeOf[standard])) +static_assert(not is_subtype_of(RegularCallableTypeOf[positional_only], RegularCallableTypeOf[keyword_only])) +static_assert(not is_subtype_of(RegularCallableTypeOf[positional_only], RegularCallableTypeOf[variadic])) +static_assert(not is_subtype_of(RegularCallableTypeOf[positional_only], RegularCallableTypeOf[keyword_variadic])) ``` #### Standard @@ -1114,13 +1114,13 @@ A standard parameter is either a positional or a keyword parameter. Unlike positional-only parameters, standard parameters should have the same name in the subtype. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def int_param_a(a: int) -> None: ... def int_param_b(b: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[int_param_a], CallableTypeOf[int_param_b])) -static_assert(not is_subtype_of(CallableTypeOf[int_param_b], CallableTypeOf[int_param_a])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_param_a], RegularCallableTypeOf[int_param_b])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_param_b], RegularCallableTypeOf[int_param_a])) ``` Apart from the name, it behaves the same as positional-only parameters. @@ -1129,8 +1129,8 @@ Apart from the name, it behaves the same as positional-only parameters. def float_param(a: float) -> None: ... def int_param(a: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[float_param], CallableTypeOf[int_param])) -static_assert(not is_subtype_of(CallableTypeOf[int_param], CallableTypeOf[float_param])) +static_assert(is_subtype_of(RegularCallableTypeOf[float_param], RegularCallableTypeOf[int_param])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_param], RegularCallableTypeOf[float_param])) ``` With the same rules for default values as well. @@ -1140,14 +1140,14 @@ def float_with_default(a: float = 1) -> None: ... def int_with_default(a: int = 1) -> None: ... def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[float_with_default], CallableTypeOf[int_with_default])) -static_assert(not is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[float_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[float_with_default], RegularCallableTypeOf[int_with_default])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[float_with_default])) -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[int_param])) -static_assert(not is_subtype_of(CallableTypeOf[int_param], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[int_param])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_param], RegularCallableTypeOf[int_with_default])) -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[int_with_default])) ``` Multiple standard parameters are checked in order along with their names: @@ -1156,8 +1156,8 @@ Multiple standard parameters are checked in order along with their names: def multi_param1(a: float, b: int, c: str) -> None: ... def multi_param2(a: int, b: bool, c: str) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_param1], CallableTypeOf[multi_param2])) -static_assert(not is_subtype_of(CallableTypeOf[multi_param2], CallableTypeOf[multi_param1])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_param1], RegularCallableTypeOf[multi_param2])) +static_assert(not is_subtype_of(RegularCallableTypeOf[multi_param2], RegularCallableTypeOf[multi_param1])) ``` The subtype can include as many standard parameters as long as they have the default value: @@ -1165,8 +1165,8 @@ The subtype can include as many standard parameters as long as they have the def ```py def multi_param_default(a: float = 1, b: int = 2, c: str = "s") -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_param_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[multi_param_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_param_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[multi_param_default])) ``` #### Standard with keyword-only @@ -1176,26 +1176,26 @@ parameter in the subtype with the same name. This is because a standard paramete than a keyword-only parameter. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def standard_a(a: int) -> None: ... def keyword_b(*, b: int) -> None: ... # The name of the parameters are different -static_assert(not is_subtype_of(CallableTypeOf[standard_a], CallableTypeOf[keyword_b])) +static_assert(not is_subtype_of(RegularCallableTypeOf[standard_a], RegularCallableTypeOf[keyword_b])) def standard_float(a: float) -> None: ... def keyword_int(*, a: int) -> None: ... # Here, the name of the parameters are the same -static_assert(is_subtype_of(CallableTypeOf[standard_float], CallableTypeOf[keyword_int])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_float], RegularCallableTypeOf[keyword_int])) def standard_with_default(a: int = 1) -> None: ... def keyword_with_default(*, a: int = 1) -> None: ... def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[standard_with_default], CallableTypeOf[keyword_with_default])) -static_assert(is_subtype_of(CallableTypeOf[standard_with_default], CallableTypeOf[empty])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_with_default], RegularCallableTypeOf[keyword_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_with_default], RegularCallableTypeOf[empty])) ``` The position of the keyword-only parameters does not matter: @@ -1204,7 +1204,7 @@ The position of the keyword-only parameters does not matter: def multi_standard(a: float, b: int, c: str) -> None: ... def multi_keyword(*, b: bool, c: str, a: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_standard], CallableTypeOf[multi_keyword])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_standard], RegularCallableTypeOf[multi_keyword])) ``` #### Standard with positional-only @@ -1214,25 +1214,25 @@ parameter in the subtype at the same position. This is because a standard parame than a positional-only parameter. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def standard_a(a: int) -> None: ... def positional_b(b: int, /) -> None: ... # The names are not important in this context -static_assert(is_subtype_of(CallableTypeOf[standard_a], CallableTypeOf[positional_b])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_a], RegularCallableTypeOf[positional_b])) def standard_float(a: float) -> None: ... def positional_int(a: int, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[standard_float], CallableTypeOf[positional_int])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_float], RegularCallableTypeOf[positional_int])) def standard_with_default(a: int = 1) -> None: ... def positional_with_default(a: int = 1, /) -> None: ... def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[standard_with_default], CallableTypeOf[positional_with_default])) -static_assert(is_subtype_of(CallableTypeOf[standard_with_default], CallableTypeOf[empty])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_with_default], RegularCallableTypeOf[positional_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_with_default], RegularCallableTypeOf[empty])) ``` The position of the positional-only parameters matter: @@ -1244,8 +1244,8 @@ def multi_positional1(b: int, c: bool, a: str, /) -> None: ... # Here, the type of the parameter `a` makes the subtype relation invalid def multi_positional2(b: int, a: float, c: str, /) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[multi_standard], CallableTypeOf[multi_positional1])) -static_assert(not is_subtype_of(CallableTypeOf[multi_standard], CallableTypeOf[multi_positional2])) +static_assert(is_subtype_of(RegularCallableTypeOf[multi_standard], RegularCallableTypeOf[multi_positional1])) +static_assert(not is_subtype_of(RegularCallableTypeOf[multi_standard], RegularCallableTypeOf[multi_positional2])) ``` #### Standard with variadic @@ -1254,14 +1254,14 @@ A variadic or keyword-variadic parameter in the supertype cannot be substituted parameter in the subtype. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def standard(a: int) -> None: ... def variadic(*a: int) -> None: ... def keyword_variadic(**a: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[standard], CallableTypeOf[variadic])) -static_assert(not is_subtype_of(CallableTypeOf[standard], CallableTypeOf[keyword_variadic])) +static_assert(not is_subtype_of(RegularCallableTypeOf[standard], RegularCallableTypeOf[variadic])) +static_assert(not is_subtype_of(RegularCallableTypeOf[standard], RegularCallableTypeOf[keyword_variadic])) ``` #### Variadic @@ -1269,13 +1269,13 @@ static_assert(not is_subtype_of(CallableTypeOf[standard], CallableTypeOf[keyword The name of the variadic parameter does not need to be the same in the subtype. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def variadic_float(*args2: float) -> None: ... def variadic_int(*args1: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic_float], CallableTypeOf[variadic_int])) -static_assert(not is_subtype_of(CallableTypeOf[variadic_int], CallableTypeOf[variadic_float])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic_float], RegularCallableTypeOf[variadic_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic_int], RegularCallableTypeOf[variadic_float])) ``` The variadic parameter does not need to be present in the supertype: @@ -1283,8 +1283,8 @@ The variadic parameter does not need to be present in the supertype: ```py def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic_int], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[variadic_int])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic_int], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[variadic_int])) ``` #### Variadic with positional-only @@ -1293,7 +1293,7 @@ If the subtype has a variadic parameter then any unmatched positional-only param supertype should be checked against the variadic parameter. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def variadic(a: int, /, *args: float) -> None: ... @@ -1303,8 +1303,8 @@ def positional_only(a: int, b: float, c: int, /) -> None: ... # Here, the parameter `b` is unmatched and there's also a variadic parameter def positional_variadic(a: int, b: float, /, *args: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic], CallableTypeOf[positional_only])) -static_assert(is_subtype_of(CallableTypeOf[variadic], CallableTypeOf[positional_variadic])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic], RegularCallableTypeOf[positional_only])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic], RegularCallableTypeOf[positional_variadic])) ``` #### Variadic with other kinds @@ -1313,7 +1313,7 @@ Variadic parameter in a subtype can only be used to match against an unmatched p parameters from the supertype, not any other parameter kind. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, is_assignable_to, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, is_assignable_to, static_assert def variadic(*args: int) -> None: ... @@ -1325,9 +1325,9 @@ def standard(a: int, b: float, /, c: int) -> None: ... def keyword_only(a: int, /, *, b: int) -> None: ... def keyword_variadic(a: int, /, **kwargs: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[variadic], CallableTypeOf[standard])) -static_assert(not is_subtype_of(CallableTypeOf[variadic], CallableTypeOf[keyword_only])) -static_assert(not is_subtype_of(CallableTypeOf[variadic], CallableTypeOf[keyword_variadic])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic], RegularCallableTypeOf[standard])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic], RegularCallableTypeOf[keyword_only])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic], RegularCallableTypeOf[keyword_variadic])) ``` But, there are special cases when matching against standard parameters. This is due to the fact that @@ -1339,8 +1339,8 @@ def variadic_keyword(*args: int, **kwargs: int) -> None: ... def standard_int(a: int) -> None: ... def standard_float(a: float) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic_keyword], CallableTypeOf[standard_int])) -static_assert(not is_subtype_of(CallableTypeOf[variadic_keyword], CallableTypeOf[standard_float])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic_keyword], RegularCallableTypeOf[standard_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic_keyword], RegularCallableTypeOf[standard_float])) ``` If the type of either the variadic or keyword-variadic parameter is not a supertype of the standard @@ -1350,8 +1350,8 @@ parameter, then the subtyping relation is invalid. def variadic_bool(*args: bool, **kwargs: int) -> None: ... def keyword_variadic_bool(*args: int, **kwargs: bool) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[variadic_bool], CallableTypeOf[standard_int])) -static_assert(not is_subtype_of(CallableTypeOf[keyword_variadic_bool], CallableTypeOf[standard_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic_bool], RegularCallableTypeOf[standard_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keyword_variadic_bool], RegularCallableTypeOf[standard_int])) ``` The standard parameter can follow a variadic parameter in the subtype. @@ -1360,8 +1360,8 @@ The standard parameter can follow a variadic parameter in the subtype. def standard_variadic_int(a: int, *args: int) -> None: ... def standard_variadic_float(a: int, *args: float) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic_keyword], CallableTypeOf[standard_variadic_int])) -static_assert(not is_subtype_of(CallableTypeOf[variadic_keyword], CallableTypeOf[standard_variadic_float])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic_keyword], RegularCallableTypeOf[standard_variadic_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic_keyword], RegularCallableTypeOf[standard_variadic_float])) ``` The keyword part of the standard parameter can be matched against keyword-only parameter with the @@ -1371,9 +1371,9 @@ same name if the keyword-variadic parameter is absent. def variadic_a(*args: int, a: int) -> None: ... def variadic_b(*args: int, b: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[variadic_a], CallableTypeOf[standard_int])) +static_assert(is_subtype_of(RegularCallableTypeOf[variadic_a], RegularCallableTypeOf[standard_int])) # The parameter name is different -static_assert(not is_subtype_of(CallableTypeOf[variadic_b], CallableTypeOf[standard_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[variadic_b], RegularCallableTypeOf[standard_int])) ``` A variadic positional parameter alone cannot match a positional-or-keyword parameter because @@ -1382,8 +1382,8 @@ variadic positional parameters can only be called positionally. ```py def only_variadic(*args: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[only_variadic], CallableTypeOf[standard_int])) -static_assert(not is_assignable_to(CallableTypeOf[only_variadic], CallableTypeOf[standard_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[only_variadic], RegularCallableTypeOf[standard_int])) +static_assert(not is_assignable_to(RegularCallableTypeOf[only_variadic], RegularCallableTypeOf[standard_int])) ``` #### Keyword-only @@ -1391,15 +1391,15 @@ static_assert(not is_assignable_to(CallableTypeOf[only_variadic], CallableTypeOf For keyword-only parameters, the name should be the same: ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def keyword_int(*, a: int) -> None: ... def keyword_float(*, a: float) -> None: ... def keyword_b(*, b: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[keyword_float], CallableTypeOf[keyword_int])) -static_assert(not is_subtype_of(CallableTypeOf[keyword_int], CallableTypeOf[keyword_float])) -static_assert(not is_subtype_of(CallableTypeOf[keyword_int], CallableTypeOf[keyword_b])) +static_assert(is_subtype_of(RegularCallableTypeOf[keyword_float], RegularCallableTypeOf[keyword_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keyword_int], RegularCallableTypeOf[keyword_float])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keyword_int], RegularCallableTypeOf[keyword_b])) ``` But, the order of the keyword-only parameters is not required to be the same: @@ -1408,28 +1408,28 @@ But, the order of the keyword-only parameters is not required to be the same: def keyword_ab(*, a: float, b: float) -> None: ... def keyword_ba(*, b: int, a: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[keyword_ab], CallableTypeOf[keyword_ba])) -static_assert(not is_subtype_of(CallableTypeOf[keyword_ba], CallableTypeOf[keyword_ab])) +static_assert(is_subtype_of(RegularCallableTypeOf[keyword_ab], RegularCallableTypeOf[keyword_ba])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keyword_ba], RegularCallableTypeOf[keyword_ab])) ``` #### Keyword-only with default ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def float_with_default(*, a: float = 1) -> None: ... def int_with_default(*, a: int = 1) -> None: ... def int_keyword(*, a: int) -> None: ... def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[float_with_default], CallableTypeOf[int_with_default])) -static_assert(not is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[float_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[float_with_default], RegularCallableTypeOf[int_with_default])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[float_with_default])) -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[int_keyword])) -static_assert(not is_subtype_of(CallableTypeOf[int_keyword], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[int_keyword])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_keyword], RegularCallableTypeOf[int_with_default])) -static_assert(is_subtype_of(CallableTypeOf[int_with_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[int_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[int_with_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[int_with_default])) ``` Keyword-only parameters with default values can be mixed with the ones without default values in any @@ -1439,20 +1439,20 @@ order: # A keyword-only parameter with a default value follows the one without a default value (it's valid) def mixed(*, b: int = 1, a: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[mixed], CallableTypeOf[int_keyword])) -static_assert(not is_subtype_of(CallableTypeOf[int_keyword], CallableTypeOf[mixed])) +static_assert(is_subtype_of(RegularCallableTypeOf[mixed], RegularCallableTypeOf[int_keyword])) +static_assert(not is_subtype_of(RegularCallableTypeOf[int_keyword], RegularCallableTypeOf[mixed])) ``` #### Keyword-only with standard ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def keywords1(*, a: int, b: int) -> None: ... def standard(b: float, a: float) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[keywords1], CallableTypeOf[standard])) -static_assert(is_subtype_of(CallableTypeOf[standard], CallableTypeOf[keywords1])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keywords1], RegularCallableTypeOf[standard])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard], RegularCallableTypeOf[keywords1])) ``` The subtype can include additional standard parameters as long as it has the default value: @@ -1461,8 +1461,8 @@ The subtype can include additional standard parameters as long as it has the def def standard_with_default(b: float, a: float, c: float = 1) -> None: ... def standard_without_default(b: float, a: float, c: float) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[standard_without_default], CallableTypeOf[keywords1])) -static_assert(is_subtype_of(CallableTypeOf[standard_with_default], CallableTypeOf[keywords1])) +static_assert(not is_subtype_of(RegularCallableTypeOf[standard_without_default], RegularCallableTypeOf[keywords1])) +static_assert(is_subtype_of(RegularCallableTypeOf[standard_with_default], RegularCallableTypeOf[keywords1])) ``` Here, we mix keyword-only parameters with standard parameters: @@ -1471,8 +1471,8 @@ Here, we mix keyword-only parameters with standard parameters: def keywords2(*, a: int, c: int, b: int) -> None: ... def mixed(b: float, a: float, *, c: float) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[keywords2], CallableTypeOf[mixed])) -static_assert(is_subtype_of(CallableTypeOf[mixed], CallableTypeOf[keywords2])) +static_assert(not is_subtype_of(RegularCallableTypeOf[keywords2], RegularCallableTypeOf[mixed])) +static_assert(is_subtype_of(RegularCallableTypeOf[mixed], RegularCallableTypeOf[keywords2])) ``` But, we shouldn't consider any unmatched positional-only parameters: @@ -1480,7 +1480,7 @@ But, we shouldn't consider any unmatched positional-only parameters: ```py def mixed_positional(b: float, /, a: float, *, c: float) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[mixed_positional], CallableTypeOf[keywords2])) +static_assert(not is_subtype_of(RegularCallableTypeOf[mixed_positional], RegularCallableTypeOf[keywords2])) ``` But, an unmatched variadic parameter is still valid: @@ -1488,7 +1488,7 @@ But, an unmatched variadic parameter is still valid: ```py def mixed_variadic(*args: float, a: float, b: float, c: float, **kwargs: float) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[mixed_variadic], CallableTypeOf[keywords2])) +static_assert(is_subtype_of(RegularCallableTypeOf[mixed_variadic], RegularCallableTypeOf[keywords2])) ``` #### Keyword-variadic @@ -1496,13 +1496,13 @@ static_assert(is_subtype_of(CallableTypeOf[mixed_variadic], CallableTypeOf[keywo The name of the keyword-variadic parameter does not need to be the same in the subtype. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def kwargs_float(**kwargs2: float) -> None: ... def kwargs_int(**kwargs1: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[kwargs_float], CallableTypeOf[kwargs_int])) -static_assert(not is_subtype_of(CallableTypeOf[kwargs_int], CallableTypeOf[kwargs_float])) +static_assert(is_subtype_of(RegularCallableTypeOf[kwargs_float], RegularCallableTypeOf[kwargs_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[kwargs_int], RegularCallableTypeOf[kwargs_float])) ``` A variadic parameter can be added in a subtype, since callers can omit it: @@ -1510,8 +1510,8 @@ A variadic parameter can be added in a subtype, since callers can omit it: ```py def empty() -> None: ... -static_assert(is_subtype_of(CallableTypeOf[kwargs_int], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[kwargs_int])) +static_assert(is_subtype_of(RegularCallableTypeOf[kwargs_int], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[kwargs_int])) ``` A positional parameter with default can also be added to the subtype, since callers can omit it: @@ -1519,24 +1519,26 @@ A positional parameter with default can also be added to the subtype, since call ```py def positional_with_default(x: int = 0) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[positional_with_default], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[positional_with_default])) +static_assert(is_subtype_of(RegularCallableTypeOf[positional_with_default], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[positional_with_default])) def positional_with_default_and_kwargs(x: int = 0, **kwargs: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[positional_with_default_and_kwargs], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[positional_with_default_and_kwargs])) +static_assert(is_subtype_of(RegularCallableTypeOf[positional_with_default_and_kwargs], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[positional_with_default_and_kwargs])) -static_assert(is_subtype_of(CallableTypeOf[positional_with_default_and_kwargs], CallableTypeOf[kwargs_int])) -static_assert(not is_subtype_of(CallableTypeOf[kwargs_int], CallableTypeOf[positional_with_default_and_kwargs])) +static_assert(is_subtype_of(RegularCallableTypeOf[positional_with_default_and_kwargs], RegularCallableTypeOf[kwargs_int])) +static_assert(not is_subtype_of(RegularCallableTypeOf[kwargs_int], RegularCallableTypeOf[positional_with_default_and_kwargs])) def positional_only_with_default_and_kwargs(x: int = 0, /, **kwargs: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[positional_only_with_default_and_kwargs], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[positional_only_with_default_and_kwargs])) +static_assert(is_subtype_of(RegularCallableTypeOf[positional_only_with_default_and_kwargs], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[positional_only_with_default_and_kwargs])) -static_assert(is_subtype_of(CallableTypeOf[positional_only_with_default_and_kwargs], CallableTypeOf[kwargs_int])) -static_assert(not is_subtype_of(CallableTypeOf[kwargs_int], CallableTypeOf[positional_only_with_default_and_kwargs])) +static_assert(is_subtype_of(RegularCallableTypeOf[positional_only_with_default_and_kwargs], RegularCallableTypeOf[kwargs_int])) +static_assert( + not is_subtype_of(RegularCallableTypeOf[kwargs_int], RegularCallableTypeOf[positional_only_with_default_and_kwargs]) +) ``` #### Keyword-variadic with keyword-only @@ -1545,14 +1547,14 @@ If the subtype has a keyword-variadic parameter then any unmatched keyword-only supertype should be checked against the keyword-variadic parameter. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def kwargs(**kwargs: float) -> None: ... def keyword_only(*, a: int, b: float, c: bool) -> None: ... def keyword_variadic(*, a: int, **kwargs: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[kwargs], CallableTypeOf[keyword_only])) -static_assert(is_subtype_of(CallableTypeOf[kwargs], CallableTypeOf[keyword_variadic])) +static_assert(is_subtype_of(RegularCallableTypeOf[kwargs], RegularCallableTypeOf[keyword_only])) +static_assert(is_subtype_of(RegularCallableTypeOf[kwargs], RegularCallableTypeOf[keyword_variadic])) ``` This is valid only for keyword-only parameters, not any other parameter kind: @@ -1563,8 +1565,8 @@ def mixed1(a: int, *, b: int) -> None: ... # Same as above but with the default value def mixed2(a: int = 1, *, b: int) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[kwargs], CallableTypeOf[mixed1])) -static_assert(not is_subtype_of(CallableTypeOf[kwargs], CallableTypeOf[mixed2])) +static_assert(not is_subtype_of(RegularCallableTypeOf[kwargs], RegularCallableTypeOf[mixed1])) +static_assert(not is_subtype_of(RegularCallableTypeOf[kwargs], RegularCallableTypeOf[mixed2])) ``` #### Empty @@ -1573,25 +1575,25 @@ When the supertype has an empty list of parameters, then the subtype can have an as long as they contain the default values for non-variadic parameters. ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def empty() -> None: ... def mixed(a: int = 1, /, b: int = 2, *args: int, c: int = 3, **kwargs: int) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[mixed], CallableTypeOf[empty])) -static_assert(not is_subtype_of(CallableTypeOf[empty], CallableTypeOf[mixed])) +static_assert(is_subtype_of(RegularCallableTypeOf[mixed], RegularCallableTypeOf[empty])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty], RegularCallableTypeOf[mixed])) ``` #### Object ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert, TypeOf +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert, TypeOf from typing import Callable def f1(a: int, b: str, /, *c: float, d: int = 1, **e: float) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[f1], object)) -static_assert(not is_subtype_of(object, CallableTypeOf[f1])) +static_assert(is_subtype_of(RegularCallableTypeOf[f1], object)) +static_assert(not is_subtype_of(object, RegularCallableTypeOf[f1])) def _( f3: Callable[[int, str], None], @@ -1613,12 +1615,12 @@ any arguments of any type, but otherwise is not a subtype or supertype of any ca ```py from typing import Callable, Never -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert def bottom(*args: object, **kwargs: object) -> Never: raise Exception() -type BottomCallable = CallableTypeOf[bottom] +type BottomCallable = RegularCallableTypeOf[bottom] static_assert(is_subtype_of(BottomCallable, Callable[..., Never])) static_assert(is_subtype_of(BottomCallable, Callable[..., int])) @@ -1634,11 +1636,11 @@ would not pass if we didn't handle this special case. ```py from typing import Callable, Any -from ty_extensions import is_subtype_of, static_assert, CallableTypeOf +from ty_extensions import is_subtype_of, static_assert, RegularCallableTypeOf def f(*args: Any, **kwargs: Any) -> Any: ... -static_assert(not is_subtype_of(CallableTypeOf[f], Callable[[], object])) +static_assert(not is_subtype_of(RegularCallableTypeOf[f], Callable[[], object])) ``` ### Classes with `__call__` @@ -2041,16 +2043,16 @@ def overloaded(x: B) -> None: ... ``` ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert from overloaded import A, B, C, overloaded def accepts_a(x: A) -> None: ... def accepts_b(x: B) -> None: ... def accepts_c(x: C) -> None: ... -static_assert(is_subtype_of(CallableTypeOf[overloaded], CallableTypeOf[accepts_a])) -static_assert(is_subtype_of(CallableTypeOf[overloaded], CallableTypeOf[accepts_b])) -static_assert(not is_subtype_of(CallableTypeOf[overloaded], CallableTypeOf[accepts_c])) +static_assert(is_subtype_of(RegularCallableTypeOf[overloaded], RegularCallableTypeOf[accepts_a])) +static_assert(is_subtype_of(RegularCallableTypeOf[overloaded], RegularCallableTypeOf[accepts_b])) +static_assert(not is_subtype_of(RegularCallableTypeOf[overloaded], RegularCallableTypeOf[accepts_c])) ``` #### Supertype overloaded @@ -2076,7 +2078,7 @@ def overloaded(a: Grandparent) -> None: ... ``` ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert from overloaded import Grandparent, Parent, Child, overloaded # This is a subtype of only the first overload @@ -2088,9 +2090,9 @@ def parent(a: Parent) -> None: ... # This is the only function that's a subtype of all overloads def grandparent(a: Grandparent) -> None: ... -static_assert(not is_subtype_of(CallableTypeOf[child], CallableTypeOf[overloaded])) -static_assert(not is_subtype_of(CallableTypeOf[parent], CallableTypeOf[overloaded])) -static_assert(is_subtype_of(CallableTypeOf[grandparent], CallableTypeOf[overloaded])) +static_assert(not is_subtype_of(RegularCallableTypeOf[child], RegularCallableTypeOf[overloaded])) +static_assert(not is_subtype_of(RegularCallableTypeOf[parent], RegularCallableTypeOf[overloaded])) +static_assert(is_subtype_of(RegularCallableTypeOf[grandparent], RegularCallableTypeOf[overloaded])) ``` #### Both overloads @@ -2142,26 +2144,26 @@ def empty_cp(a: Parent) -> None: ... ``` ```py -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert from overloaded import pg, po, go, cpg, empty_go, empty_cp -static_assert(is_subtype_of(CallableTypeOf[pg], CallableTypeOf[cpg])) -static_assert(is_subtype_of(CallableTypeOf[cpg], CallableTypeOf[pg])) +static_assert(is_subtype_of(RegularCallableTypeOf[pg], RegularCallableTypeOf[cpg])) +static_assert(is_subtype_of(RegularCallableTypeOf[cpg], RegularCallableTypeOf[pg])) -static_assert(not is_subtype_of(CallableTypeOf[po], CallableTypeOf[pg])) -static_assert(not is_subtype_of(CallableTypeOf[pg], CallableTypeOf[po])) +static_assert(not is_subtype_of(RegularCallableTypeOf[po], RegularCallableTypeOf[pg])) +static_assert(not is_subtype_of(RegularCallableTypeOf[pg], RegularCallableTypeOf[po])) -static_assert(is_subtype_of(CallableTypeOf[go], CallableTypeOf[pg])) -static_assert(not is_subtype_of(CallableTypeOf[pg], CallableTypeOf[go])) +static_assert(is_subtype_of(RegularCallableTypeOf[go], RegularCallableTypeOf[pg])) +static_assert(not is_subtype_of(RegularCallableTypeOf[pg], RegularCallableTypeOf[go])) # Overload 1 in `empty_go` is a subtype of overload 1 in `empty_cp` # Overload 2 in `empty_go` is a subtype of overload 2 in `empty_cp` # Overload 2 in `empty_go` is a subtype of overload 3 in `empty_cp` # # All overloads in `empty_cp` has a subtype in `empty_go` -static_assert(is_subtype_of(CallableTypeOf[empty_go], CallableTypeOf[empty_cp])) +static_assert(is_subtype_of(RegularCallableTypeOf[empty_go], RegularCallableTypeOf[empty_cp])) -static_assert(not is_subtype_of(CallableTypeOf[empty_cp], CallableTypeOf[empty_go])) +static_assert(not is_subtype_of(RegularCallableTypeOf[empty_cp], RegularCallableTypeOf[empty_go])) ``` #### Order of overloads @@ -2188,10 +2190,10 @@ def overload_ba(x: A) -> None: ... ```py from overloaded import overload_ab, overload_ba -from ty_extensions import CallableTypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, is_subtype_of, static_assert -static_assert(is_subtype_of(CallableTypeOf[overload_ab], CallableTypeOf[overload_ba])) -static_assert(is_subtype_of(CallableTypeOf[overload_ba], CallableTypeOf[overload_ab])) +static_assert(is_subtype_of(RegularCallableTypeOf[overload_ab], RegularCallableTypeOf[overload_ba])) +static_assert(is_subtype_of(RegularCallableTypeOf[overload_ba], RegularCallableTypeOf[overload_ab])) ``` ### Generic callables @@ -2203,7 +2205,7 @@ the generic callable.) ```py from typing import Callable -from ty_extensions import CallableTypeOf, TypeOf, is_subtype_of, static_assert +from ty_extensions import RegularCallableTypeOf, TypeOf, is_subtype_of, static_assert def identity[T](t: T) -> T: return t @@ -2222,11 +2224,11 @@ static_assert(not is_subtype_of(TypeOf[identity], Callable[[str], int])) # TODO: no error # error: [static-assert-error] -static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[int], int])) +static_assert(is_subtype_of(RegularCallableTypeOf[identity], Callable[[int], int])) # TODO: no error # error: [static-assert-error] -static_assert(is_subtype_of(CallableTypeOf[identity], Callable[[str], str])) -static_assert(not is_subtype_of(CallableTypeOf[identity], Callable[[str], int])) +static_assert(is_subtype_of(RegularCallableTypeOf[identity], Callable[[str], str])) +static_assert(not is_subtype_of(RegularCallableTypeOf[identity], Callable[[str], int])) ``` The reverse is not true — if someone expects a generic function that can be called with any @@ -2237,9 +2239,9 @@ static_assert(not is_subtype_of(Callable[[int], int], TypeOf[identity])) static_assert(not is_subtype_of(Callable[[str], str], TypeOf[identity])) static_assert(not is_subtype_of(Callable[[str], int], TypeOf[identity])) -static_assert(not is_subtype_of(Callable[[int], int], CallableTypeOf[identity])) -static_assert(not is_subtype_of(Callable[[str], str], CallableTypeOf[identity])) -static_assert(not is_subtype_of(Callable[[str], int], CallableTypeOf[identity])) +static_assert(not is_subtype_of(Callable[[int], int], RegularCallableTypeOf[identity])) +static_assert(not is_subtype_of(Callable[[str], str], RegularCallableTypeOf[identity])) +static_assert(not is_subtype_of(Callable[[str], int], RegularCallableTypeOf[identity])) ``` ## String literals and Sequence diff --git a/crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md b/crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md index 115c6361c4e0e..27c41fc27461d 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md +++ b/crates/ty_python_semantic/resources/mdtest/type_properties/materialization.md @@ -237,7 +237,7 @@ Materializing an overloaded callable materializes each overload separately. ```py from typing import overload -from ty_extensions import CallableTypeOf +from ty_extensions import RegularCallableTypeOf @overload def f(x: int) -> Any: ... @@ -246,7 +246,7 @@ def f(*args: Any, **kwargs: Any) -> str: ... def f(*args: object, **kwargs: object) -> object: pass -def _(top: Top[CallableTypeOf[f]], bottom: Bottom[CallableTypeOf[f]]): +def _(top: Top[RegularCallableTypeOf[f]], bottom: Bottom[RegularCallableTypeOf[f]]): reveal_type(top) # revealed: Overload[(x: int) -> object, Top[(...) -> str]] reveal_type(bottom) # revealed: Overload[(x: int) -> Never, (*args: object, **kwargs: object) -> str] ``` @@ -271,7 +271,7 @@ type TopCallable = Top[Callable[..., Any]] def takes_objects(*args: object, **kwargs: object) -> object: pass -static_assert(not is_subtype_of(TopCallable, CallableTypeOf[takes_objects])) +static_assert(not is_subtype_of(TopCallable, RegularCallableTypeOf[takes_objects])) ``` ## Tuple diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index a1019b390f7ac..f6afce5a5dbcf 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -1293,11 +1293,20 @@ impl<'db> Bindings<'db> { } } - Some(KnownFunction::IntoCallable) => { + Some( + into_callable @ (KnownFunction::IntoCallable + | KnownFunction::IntoRegularCallable), + ) => { let [Some(ty)] = overload.parameter_types() else { continue; }; - let Some(callables) = ty.try_upcast_to_callable(db) else { + let Some(callables) = ty.try_upcast_to_callable(db).map(|callables| { + if into_callable == KnownFunction::IntoRegularCallable { + callables.map(|callable| callable.into_regular(db)) + } else { + callables + } + }) else { continue; }; overload.set_return_type(callables.into_type(db)); diff --git a/crates/ty_python_semantic/src/types/callable.rs b/crates/ty_python_semantic/src/types/callable.rs index 93e787e3395c9..52769789f634b 100644 --- a/crates/ty_python_semantic/src/types/callable.rs +++ b/crates/ty_python_semantic/src/types/callable.rs @@ -292,6 +292,10 @@ impl<'db> CallableType<'db> { matches!(self.kind(db), CallableTypeKind::StaticMethodLike) } + pub(crate) fn into_regular(self, db: &'db dyn Db) -> CallableType<'db> { + CallableType::new(db, self.signatures(db), CallableTypeKind::Regular) + } + pub(crate) fn bind_self( self, db: &'db dyn Db, diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index bfc189535ac18..22a730a33ec8f 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -226,6 +226,7 @@ impl<'db> ClassBase<'db> { | SpecialFormType::Intersection | SpecialFormType::TypeOf | SpecialFormType::CallableTypeOf + | SpecialFormType::RegularCallableTypeOf | SpecialFormType::AlwaysTruthy | SpecialFormType::AlwaysFalsy => None, diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 4a1dc8ef700b7..c70c2de510714 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -1720,6 +1720,8 @@ pub enum KnownFunction { GenericContext, /// `ty_extensions.into_callable` IntoCallable, + /// `ty_extensions.into_regular_callable` + IntoRegularCallable, /// `ty_extensions.dunder_all_names` DunderAllNames, /// `ty_extensions.enum_members` @@ -1807,6 +1809,7 @@ impl KnownFunction { | Self::IsSubtypeOf | Self::GenericContext | Self::IntoCallable + | Self::IntoRegularCallable | Self::DunderAllNames | Self::EnumMembers | Self::StaticAssert @@ -2294,6 +2297,7 @@ pub(crate) mod tests { | KnownFunction::IsSubtypeOf | KnownFunction::GenericContext | KnownFunction::IntoCallable + | KnownFunction::IntoRegularCallable | KnownFunction::DunderAllNames | KnownFunction::EnumMembers | KnownFunction::StaticAssert diff --git a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs index c661519ee4944..d1f2e4be3e576 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs @@ -1752,7 +1752,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { type_of_type } - SpecialFormType::CallableTypeOf => { + SpecialFormType::CallableTypeOf | SpecialFormType::RegularCallableTypeOf => { let arguments = if let ast::Expr::Tuple(tuple) = arguments_slice { &*tuple.elts } else { @@ -1779,9 +1779,16 @@ impl<'db> TypeInferenceBuilder<'db, '_> { let argument_type = self.infer_expression(&arguments[0], TypeContext::default()); - let Some(callable_type) = argument_type - .try_upcast_to_callable(db) - .map(|callables| callables.into_type(self.db())) + let Some(callable_type) = + argument_type.try_upcast_to_callable(db).map(|callables| { + if special_form == SpecialFormType::RegularCallableTypeOf { + callables + .map(|callable| callable.into_regular(db)) + .into_type(db) + } else { + callables.into_type(db) + } + }) else { if let Some(builder) = self .context diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index 7a0966c3ac4c7..0063a2ce91608 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -79,6 +79,8 @@ pub enum SpecialFormType { TypeOf, /// The symbol `ty_extensions.CallableTypeOf` CallableTypeOf, + /// The symbol `ty_extensions.RegularCallableTypeOf` + RegularCallableTypeOf, /// The symbol `ty_extensions.Top` Top, /// The symbol `ty_extensions.Bottom` @@ -143,6 +145,7 @@ impl SpecialFormType { | Self::Bottom | Self::Intersection | Self::CallableTypeOf + | Self::RegularCallableTypeOf | Self::Unknown | Self::AlwaysTruthy | Self::AlwaysFalsy @@ -213,6 +216,7 @@ impl SpecialFormType { Intersection, TypeOf, CallableTypeOf, + RegularCallableTypeOf, Top, Bottom, #[strum(serialize = "Self")] @@ -253,6 +257,7 @@ impl SpecialFormType { SpecialFormType::Annotated => Self::Annotated, SpecialFormType::Callable => Self::Callable, SpecialFormType::CallableTypeOf => Self::CallableTypeOf, + SpecialFormType::RegularCallableTypeOf => Self::RegularCallableTypeOf, SpecialFormType::Concatenate => Self::Concatenate, SpecialFormType::Intersection => Self::Intersection, SpecialFormType::Literal => Self::Literal, @@ -308,6 +313,7 @@ impl SpecialFormType { SpecialFormTypeBuilder::Annotated => Self::Annotated, SpecialFormTypeBuilder::Callable => Self::Callable, SpecialFormTypeBuilder::CallableTypeOf => Self::CallableTypeOf, + SpecialFormTypeBuilder::RegularCallableTypeOf => Self::RegularCallableTypeOf, SpecialFormTypeBuilder::Concatenate => Self::Concatenate, SpecialFormTypeBuilder::Intersection => Self::Intersection, SpecialFormTypeBuilder::Literal => Self::Literal, @@ -409,7 +415,8 @@ impl SpecialFormType { | Self::Bottom | Self::Intersection | Self::TypeOf - | Self::CallableTypeOf => module.is_ty_extensions(), + | Self::CallableTypeOf + | Self::RegularCallableTypeOf => module.is_ty_extensions(), } } @@ -464,6 +471,7 @@ impl SpecialFormType { | Self::Intersection | Self::TypeOf | Self::CallableTypeOf + | Self::RegularCallableTypeOf | Self::Callable | Self::TypingSelf | Self::TypeQualifier(_) @@ -494,6 +502,7 @@ impl SpecialFormType { | Self::Annotated | Self::Bottom | Self::CallableTypeOf + | Self::RegularCallableTypeOf | Self::TypeQualifier(_) | Self::Concatenate | Self::Intersection @@ -560,6 +569,7 @@ impl SpecialFormType { SpecialFormType::Intersection => "Intersection", SpecialFormType::TypeOf => "TypeOf", SpecialFormType::CallableTypeOf => "CallableTypeOf", + SpecialFormType::RegularCallableTypeOf => "RegularCallableTypeOf", SpecialFormType::Top => "Top", SpecialFormType::Bottom => "Bottom", SpecialFormType::Protocol => "Protocol", @@ -604,6 +614,7 @@ impl SpecialFormType { | SpecialFormType::Intersection | SpecialFormType::TypeOf | SpecialFormType::CallableTypeOf + | SpecialFormType::RegularCallableTypeOf | SpecialFormType::Top | SpecialFormType::Bottom => &[KnownModule::TyExtensions], } @@ -709,7 +720,8 @@ impl SpecialFormType { | Self::TypeIs | Self::TypeGuard | Self::Unpack - | Self::CallableTypeOf => Err(InvalidTypeExpressionError { + | Self::CallableTypeOf + | Self::RegularCallableTypeOf => Err(InvalidTypeExpressionError { invalid_expressions: smallvec::smallvec_inline![ InvalidTypeExpression::RequiresOneArgument(self) ], diff --git a/crates/ty_vendored/ty_extensions/ty_extensions.pyi b/crates/ty_vendored/ty_extensions/ty_extensions.pyi index 9d6313744e327..76ecb28af01d4 100644 --- a/crates/ty_vendored/ty_extensions/ty_extensions.pyi +++ b/crates/ty_vendored/ty_extensions/ty_extensions.pyi @@ -20,6 +20,26 @@ Not: _SpecialForm Intersection: _SpecialForm TypeOf: _SpecialForm CallableTypeOf: _SpecialForm +""" +`CallableTypeOf[T]` extracts the callable type of `T` while preserving any function-like +behavior. + +This means the result may behave differently from a plain `typing.Callable` in +type-theoretic checks. In particular, method-like and descriptor-like callables remain +distinct from regular callables. + +Use this when you want to preserve the full callable flavor of a function, method, or +synthesized callable. +""" + +RegularCallableTypeOf: _SpecialForm +""" +`RegularCallableTypeOf[T]` extracts a regular `typing.Callable`-style type from `T`. + +This keeps the callable signatures of `T` but discards function-like behavior such as +descriptor-style method binding. Use this when you want to compare a callable against +ordinary `Callable[...]` types in type-theoretic tests. +""" Top: _SpecialForm """ @@ -196,6 +216,12 @@ def into_callable(ty: Any) -> Any: This is the value equivalent of `CallableTypeOf`, which operates on types. """ +def into_regular_callable(ty: Any) -> Any: + """Converts a value into a regular `Callable`, if possible. + + This is the value equivalent of `RegularCallableTypeOf`, which operates on types. + """ + def dunder_all_names(module: Any) -> tuple[LiteralString, ...] | None: """Returns the `__all__` names of a module as a tuple of sorted strings.