Skip to content

Commit b0d9fe5

Browse files
committed
multiple nested case
1 parent ecfab6a commit b0d9fe5

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

mypy/semanal.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6408,9 +6408,32 @@ def lookup_fully_qualified_or_none(self, fullname: str) -> SymbolTableNode | Non
64086408
# TODO: support nested classes (but consider performance impact,
64096409
# we might keep the module level only lookup for thing like 'builtins.int').
64106410
assert "." in fullname
6411+
64116412
module, name = fullname.rsplit(".", maxsplit=1)
6413+
6414+
# The reason for this is that we want to be able to handle cases such as importlib.machinery
64126415
if module not in self.modules:
6413-
return None
6416+
# Check if there's nested module A.B.C
6417+
splitted = fullname.rsplit(".")
6418+
module, name = splitted[0], splitted[1:]
6419+
# If module still not in modules, return None
6420+
if module not in self.modules:
6421+
return None
6422+
filenode = self.modules[module]
6423+
result = filenode.names.get(name[0])
6424+
6425+
if result is None and self.is_incomplete_namespace(module):
6426+
# TODO: More explicit handling of incomplete refs?
6427+
self.record_incomplete_ref()
6428+
6429+
for part in name[1:]:
6430+
if result is not None and isinstance(result.node, TypeInfo):
6431+
filenode = result.node
6432+
result = filenode.names.get(part)
6433+
else:
6434+
return None
6435+
return result
6436+
64146437
filenode = self.modules[module]
64156438
result = filenode.names.get(name)
64166439
if result is None and self.is_incomplete_namespace(module):

test-data/unit/check-python312.test

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1873,3 +1873,63 @@ d1: Multi[int, str] = Multi[float, str]() # E: Incompatible types in assignment
18731873
d2: Multi[float, str] = Multi[int, str]() # E: Incompatible types in assignment (expression has type "Multi[int, str]", variable has type "Multi[float, str]")
18741874
d3: Multi[str, int] = Multi[str, float]()
18751875
d4: Multi[str, float] = Multi[str, int]() # E: Incompatible types in assignment (expression has type "Multi[str, int]", variable has type "Multi[str, float]")
1876+
1877+
[case testPEP695MultipleNestedGenericClass1]
1878+
# flags: --enable-incomplete-feature=NewGenericSyntax
1879+
class A:
1880+
class B:
1881+
class C:
1882+
class D[Q]:
1883+
def g(self, x: Q): ...
1884+
d: D[str]
1885+
1886+
x: A.B.C.D[int]
1887+
x.g('a') # E: Argument 1 to "g" of "D" has incompatible type "str"; expected "int"
1888+
reveal_type(x) # N: Revealed type is "__main__.A.B.C.D[builtins.int]"
1889+
reveal_type(A.B.C.d) # N: Revealed type is "__main__.A.B.C.D[builtins.str]"
1890+
1891+
[case testPEP695MultipleNestedGenericClass2]
1892+
# flags: --enable-incomplete-feature=NewGenericSyntax
1893+
class A:
1894+
class B:
1895+
def m(self) -> None:
1896+
class C[T]:
1897+
def f(self) -> T: ...
1898+
x: C[int]
1899+
reveal_type(x.f()) # N: Revealed type is "builtins.int"
1900+
self.a = C[str]()
1901+
1902+
reveal_type(A().B().a) # N: Revealed type is "__main__.C@5[builtins.str]"
1903+
1904+
[case testPEP695MultipleNestedGenericClass3]
1905+
# flags: --enable-incomplete-feature=NewGenericSyntax
1906+
class A:
1907+
class C[T]:
1908+
def f(self) -> T: ...
1909+
class D[S]:
1910+
x: T # E: Name "T" is not defined
1911+
def g(self) -> S: ...
1912+
1913+
a: A.C[int]
1914+
reveal_type(a.f()) # N: Revealed type is "builtins.int"
1915+
b: A.C.D[str]
1916+
reveal_type(b.g()) # N: Revealed type is "builtins.str"
1917+
1918+
class B:
1919+
class E[T]:
1920+
class F[T]: # E: "T" already defined as a type parameter
1921+
x: T
1922+
1923+
c: B.E.F[int]
1924+
1925+
[case testPEP695MultipleNestedGenericClass4]
1926+
# flags: --enable-incomplete-feature=NewGenericSyntax
1927+
class Z:
1928+
class A:
1929+
class B[T]:
1930+
def __get__(self, instance: Z.A, owner: type[Z.A]) -> T:
1931+
return None # E: Incompatible return value type (got "None", expected "T")
1932+
f = B[int]()
1933+
1934+
a = Z.A()
1935+
v = a.f

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,23 @@ def f(x: int) -> None: pass
9595
[out]
9696
==
9797
main:7: error: Missing positional argument "x" in call to "f"
98+
99+
[case testPEP695MultipleNestedGenericClassMethodUpdated]
100+
from a import f
101+
102+
class A:
103+
class C:
104+
class D[T]:
105+
x: T
106+
def m(self) -> T:
107+
f()
108+
return self.x
109+
110+
[file a.py]
111+
def f() -> None: pass
112+
113+
[file a.py.2]
114+
def f(x: int) -> None: pass
115+
[out]
116+
==
117+
main:8: error: Missing positional argument "x" in call to "f"

0 commit comments

Comments
 (0)