Skip to content

Commit f68f76d

Browse files
authored
[PEP 695] Fix nested generic classes (#17776)
There was confusion about the fullnames of type variables in nested generic classes. A type variable could be defined internally as `m.OuterClass.T`, but it was sometimes accessed as `m.T`. The root cause was that the semantic analyzer didn't initialize the attribute that refers to the enclosing class consistently. Fixes #17596. Fixes #17630.
1 parent 5c38427 commit f68f76d

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

mypy/semanal.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,11 @@ def file_context(
824824
self.num_incomplete_refs = 0
825825

826826
if active_type:
827+
enclosing_fullname = active_type.fullname.rsplit(".", 1)[0]
828+
if "." in enclosing_fullname:
829+
enclosing_node = self.lookup_fully_qualified_or_none(enclosing_fullname)
830+
if enclosing_node and isinstance(enclosing_node.node, TypeInfo):
831+
self._type = enclosing_node.node
827832
self.push_type_args(active_type.defn.type_args, active_type.defn)
828833
self.incomplete_type_stack.append(False)
829834
scope.enter_class(active_type)

test-data/unit/check-python312.test

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,3 +1713,69 @@ type XNested = (1 + (yield 1)) # E: Yield expression cannot be used within a ty
17131713
type YNested = (1 + (yield from [])) # E: Yield expression cannot be used within a type alias
17141714
type ZNested = (1 + (a := 1)) # E: Named expression cannot be used within a type alias
17151715
type KNested = (1 + (await 1)) # E: Await expression cannot be used within a type alias
1716+
1717+
[case testPEP695NestedGenericClass1]
1718+
# flags: --enable-incomplete-feature=NewGenericSyntax
1719+
class C[T]:
1720+
def f(self) -> T: ...
1721+
1722+
class A:
1723+
class B[Q]:
1724+
def __init__(self, a: Q) -> None:
1725+
self.a = a
1726+
1727+
def f(self) -> Q:
1728+
return self.a
1729+
1730+
def g(self, x: Q) -> None: ...
1731+
1732+
b: B[str]
1733+
1734+
x: A.B[int]
1735+
x.g("x") # E: Argument 1 to "g" of "B" has incompatible type "str"; expected "int"
1736+
reveal_type(x.a) # N: Revealed type is "builtins.int"
1737+
reveal_type(x) # N: Revealed type is "__main__.A.B[builtins.int]"
1738+
reveal_type(A.b) # N: Revealed type is "__main__.A.B[builtins.str]"
1739+
1740+
[case testPEP695NestedGenericClass2]
1741+
# flags: --enable-incomplete-feature=NewGenericSyntax
1742+
class A:
1743+
def m(self) -> None:
1744+
class B[T]:
1745+
def f(self) -> T: ...
1746+
x: B[int]
1747+
reveal_type(x.f()) # N: Revealed type is "builtins.int"
1748+
self.a = B[str]()
1749+
1750+
reveal_type(A().a) # N: Revealed type is "__main__.B@4[builtins.str]"
1751+
reveal_type(A().a.f()) # N: Revealed type is "builtins.str"
1752+
1753+
[case testPEP695NestedGenericClass3]
1754+
# flags: --enable-incomplete-feature=NewGenericSyntax
1755+
class C[T]:
1756+
def f(self) -> T: ...
1757+
class D[S]:
1758+
x: T # E: Name "T" is not defined
1759+
def g(self) -> S: ...
1760+
1761+
a: C[int]
1762+
reveal_type(a.f()) # N: Revealed type is "builtins.int"
1763+
b: C.D[str]
1764+
reveal_type(b.g()) # N: Revealed type is "builtins.str"
1765+
1766+
class E[T]:
1767+
class F[T]: # E: "T" already defined as a type parameter
1768+
x: T
1769+
1770+
c: E.F[int]
1771+
1772+
[case testPEP695NestedGenericClass4]
1773+
# flags: --enable-incomplete-feature=NewGenericSyntax
1774+
class A:
1775+
class B[T]:
1776+
def __get__(self, instance: A, owner: type[A]) -> T:
1777+
return None # E: Incompatible return value type (got "None", expected "T")
1778+
f = B[int]()
1779+
1780+
a = A()
1781+
v = a.f

test-data/unit/fine-grained-python312.test

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,23 @@ from builtins import tuple as B
8080
==
8181
main:4: error: Incompatible types in assignment (expression has type "int", variable has type "tuple[int, str]")
8282
main:5: error: Incompatible types in assignment (expression has type "str", variable has type "tuple[int, str]")
83+
84+
[case testPEP695NestedGenericClassMethodUpdated]
85+
# flags: --enable-incomplete-feature=NewGenericSyntax
86+
from a import f
87+
88+
class C:
89+
class D[T]:
90+
x: T
91+
def m(self) -> T:
92+
f()
93+
return self.x
94+
95+
[file a.py]
96+
def f() -> None: pass
97+
98+
[file a.py.2]
99+
def f(x: int) -> None: pass
100+
[out]
101+
==
102+
main:8: error: Missing positional argument "x" in call to "f"

0 commit comments

Comments
 (0)