-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Type parameter scoping seems to disagree with PEP 484 #11993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Labels
Comments
Could you add a complete code sample that illustrates the behavior you find problematic? |
from typing import Generic, TypeVar
T = TypeVar('T')
class A(Generic[T]):
def m(self, x: T) -> list[T]:
return [x]
f = A[int].m
f(A[int](), 0) # Signature of f should be (self: A[int], x: int) -> list[int]
g = A.m
g(A(), 0) # Signature of g should be (self: A[Any], x: Any) -> list[Any] This program should type check. Mypy (version 0.931) reports:
|
cdce8p
pushed a commit
to cdce8p/mypy
that referenced
this issue
May 31, 2025
Fixes python#18024 Fixes python#18706 Fixes python#17734 Fixes python#15097 Fixes python#14814 Fixes python#14806 Fixes python#14259 Fixes python#13041 Fixes python#11993 Fixes python#9585 Fixes python#9266 Fixes python#9202 Fixes python#5481 This is a fourth "major" PR toward python#7724. This is one is watershed/crux of the whole series (but to set correct expectations, there are almost a dozen smaller follow-up/clean-up PRs in the pipeline). The core of the idea is to set current type-checker as part of the global state. There are however some details: * There are cases where we call `is_subtype()` before type-checking. For now, I fall back to old logic in this cases. In follow up PRs we may switch to using type-checker instances before type checking phase (this requires some care). * This increases typeops import cycle by a few modules, but unfortunately this is inevitable. * This PR increases potential for infinite recursion in protocols. To mitigate I add: one legitimate fix for `__call__`, and one temporary hack for `freshen_all_functions_type_vars` (to reduce performance impact). * Finally I change semantics for method access on class objects to match the one in old `find_member()`. Now we will expand type by instance, so we have something like this: ```python class B(Generic[T]): def foo(self, x: T) -> T: ... class C(B[str]): ... reveal_type(C.foo) # def (self: B[str], x: str) -> str ``` FWIW, I am not even 100% sure this is correct, it seems to me we _may_ keep the method generic. But in any case what we do currently is definitely wrong (we infer a _non-generic_ `def (x: T) -> T`). --------- Co-authored-by: hauntsaninja <[email protected]> Co-authored-by: Shantanu <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
PEP 484 has: "A type variable used in a method of a generic class that coincides with one of the variables that parameterize this class is always bound to that variable." The example there has:
This should mean that T occurs bound in the signature of meth_1 (it is bound to the type parameter of the generic class MyClass).
The signature of MyClass[int].meth_1 should be
(self:MyClass[int],x:int)->int
. This is gotten from the signature of the function in the generic class with the type argument int substituted for occurrences of the type parameter T.The signature of MyClass.meth_1 should be
(self:MyClass[Any],x:Any)->Any
. The generic class is implicitly instantiated to Any. Further, we must substitute something for the occurrences of the type parameter in the signature (the type parameter can't become a free occurrence just by looking at it from outside the class).Mypy seems to do something else. The type of MyClass[int].meth_1 is revealed as:
Instead of substituting int for T it has used some other type (maybe a free type variable?). If we try to apply it to an instance of MyClass[int] and an int, we get that the type of the application is T`1 whatever that is, and we get errors:
The type of MyClass.meth_1 is revealed as:
This looks like it might be a generic function, but MyClass.meth_1 should not be generic (it has no free type variables). There is actually no occurrence of the type parameter T in the signature, it seems to have been instantiated to T`1 whatever that is (unless T`1 is another spelling of T).
This function does behave as if it were generic, actually. We can apply it to an instance of MyClass[int] and an int and the type of the application is int. And it's apparently not an instantiation to Any because if we try to apply it to an instance of MyClass[int] and a str it is a type error "Cannot infer function argument".
What I think is happening is that mypy is treating functions in generic classes as themselves generic functions parameterized over the class's type parameters. This seems wrong or else I've completely misunderstood PEP 484.
The text was updated successfully, but these errors were encountered: