CallableFunction as a drop-in replacement for Callable
#2191
Replies: 2 comments
-
Three cases when the type doesn't workty says invalid-assignment in all three cases. See playground. One function is decorated with functools.cache and Pylance says: Type "list[_lru_cache_wrapper[int] | ((dd: int, /) -> int)]" is not assignable to declared type "list[CallableFunction[(int), int]]"
"_lru_cache_wrapper[int]" is incompatible with protocol "CallableFunction[(int), int]"
"__closure__" is not present
"__code__" is not present
"__defaults__" is not present
"__globals__" is not present
"__name__" is not present
"__annotate__" is not present
"__kwdefaults__" is not presentIn the other two cases, the functions are not decorated, and Pylance objects to # Code
direction: CallableFunction[[int], int] = pos if 0 <=index else neg
# Diagnostic message
Type "((a: _SupportsPos[_T_co@pos], /) -> _T_co@pos) | ((a: _SupportsNeg[_T_co@neg], /) -> _T_co@neg)" is not assignable to declared type "CallableFunction[(int), int]"
Type "((a: _SupportsPos[_T_co@pos], /) -> _T_co@pos) | ((a: _SupportsNeg[_T_co@neg], /) -> _T_co@neg)" is not assignable to type "CallableFunction[(int), int]"
"FunctionType" is incompatible with protocol "CallableFunction[(int), int]"
"__wrapped__" is not present
"CallableFunction[P@CallableFunction, R@CallableFunction]" is not assignable to "FunctionType"
"CallableFunction[P@CallableFunction, R@CallableFunction]" is not assignable to "FunctionType"
"CallableFunction[P@CallableFunction, R@CallableFunction]" is not assignable to "FunctionType"If I remove |
Beta Was this translation helpful? Give feedback.
-
|
Python’s static typing system cannot currently infer a generic return type from an “indirect” factory method unless the type variable is directly tied to an input parameter. That’s a limitation of how type variables are propagated in Python’s type checkers and the language’s typing design. In particular: Type variables (TypeVar) are only meaningful to type checkers when they are tied to a function’s signature in a way the checker can follow — usually as function arguments or bound to a class. If you write a factory function that returns a type based on some internal logic without any connection between the return type and the signature’s type parameters, type checkers have no basis to infer the correct return type at call sites. Python’s typing does not support “higher kinded types” or type inference from indirect logic — this is something discussed frequently but unresolved in the core typing design. The canonical pattern that does work and is fully supported today looks like this: T = TypeVar("T") def create_instance(cls: Type[T]) -> T: In this pattern: The cls: Type[T] parameter connects the generic type variable T to an input parameter. Static checkers like mypy or PyCharm can then reason that create_instance(Foo) returns a Foo. Without this connection, no current type checker (including mypy, pyright, or the built-in stubs) can reliably infer the return type. So although Python’s typing library has evolved significantly, there is no built-in mechanism today that allows a factory function to magically infer its generic return type from indirect logic alone. The recommended workaround is to ensure the type variable appears in the function signature in a way type checkers understand. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Not all
Callablehave the__name__attribute. See, for example, "Why does ty sayCallablehas no attribute__name__?". That looks like a difficult problem to solve, so I've attempted to create a type I can use temporarily while the experienced programmers work on a permanent solution.I learned about this issue because of a function in my tests. The type below works as a replacement for my one function. See ty playground. But that doesn't mean much.
I've mashed-up annotations from typeshed, then "refined" the code by running it through different LLMs and trying to separate the good advice from the bad advice.
Frankly, this is beyond my ability to evaluate. The type below may be so flawed that I should start over. If this is a decent start, I don't know how to find the parts that need improvement. Therefore, I would love some feedback.
Beta Was this translation helpful? Give feedback.
All reactions