Skip to content

Commit 520b83e

Browse files
authored
Treat Any metaclass the same as Any base class (#13605)
Closes #13599
1 parent c0372cc commit 520b83e

File tree

2 files changed

+41
-15
lines changed

2 files changed

+41
-15
lines changed

mypy/semanal.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,12 +2076,17 @@ def get_declared_metaclass(
20762076
# Probably a name error - it is already handled elsewhere
20772077
return None, False
20782078
if isinstance(sym.node, Var) and isinstance(get_proper_type(sym.node.type), AnyType):
2079-
# 'Any' metaclass -- just ignore it.
2080-
#
2081-
# TODO: A better approach would be to record this information
2082-
# and assume that the type object supports arbitrary
2083-
# attributes, similar to an 'Any' base class.
2084-
return None, False
2079+
# Create a fake TypeInfo that fallbacks to `Any`, basically allowing
2080+
# all the attributes. Same thing as we do for `Any` base class.
2081+
any_info = self.make_empty_type_info(ClassDef(sym.node.name, Block([])))
2082+
any_info.fallback_to_any = True
2083+
any_info._fullname = sym.node.fullname
2084+
if self.options.disallow_subclassing_any:
2085+
self.fail(
2086+
f'Class cannot use "{any_info.fullname}" as a metaclass (has type "Any")',
2087+
metaclass_expr,
2088+
)
2089+
return Instance(any_info, []), False
20852090
if isinstance(sym.node, PlaceholderNode):
20862091
return None, True # defer later in the caller
20872092

test-data/unit/check-classes.test

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,6 +4392,35 @@ def f(TB: Type[B]):
43924392
reveal_type(TB) # N: Revealed type is "Type[__main__.B]"
43934393
reveal_type(TB.x) # N: Revealed type is "builtins.int"
43944394

4395+
[case testMetaclassAsAny]
4396+
from typing import Any, ClassVar
4397+
4398+
MyAny: Any
4399+
class WithMeta(metaclass=MyAny):
4400+
x: ClassVar[int]
4401+
4402+
reveal_type(WithMeta.a) # N: Revealed type is "Any"
4403+
reveal_type(WithMeta.m) # N: Revealed type is "Any"
4404+
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
4405+
reveal_type(WithMeta().x) # N: Revealed type is "builtins.int"
4406+
WithMeta().m # E: "WithMeta" has no attribute "m"
4407+
WithMeta().a # E: "WithMeta" has no attribute "a"
4408+
4409+
[case testMetaclassAsAnyWithAFlag]
4410+
# flags: --disallow-subclassing-any
4411+
from typing import Any, ClassVar
4412+
4413+
MyAny: Any
4414+
class WithMeta(metaclass=MyAny): # E: Class cannot use "__main__.MyAny" as a metaclass (has type "Any")
4415+
x: ClassVar[int]
4416+
4417+
reveal_type(WithMeta.a) # N: Revealed type is "Any"
4418+
reveal_type(WithMeta.m) # N: Revealed type is "Any"
4419+
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
4420+
reveal_type(WithMeta().x) # N: Revealed type is "builtins.int"
4421+
WithMeta().m # E: "WithMeta" has no attribute "m"
4422+
WithMeta().a # E: "WithMeta" has no attribute "a"
4423+
43954424
[case testMetaclassIterable]
43964425
from typing import Iterable, Iterator
43974426

@@ -4476,15 +4505,7 @@ from missing import M
44764505
class A(metaclass=M):
44774506
y = 0
44784507
reveal_type(A.y) # N: Revealed type is "builtins.int"
4479-
A.x # E: "Type[A]" has no attribute "x"
4480-
4481-
[case testAnyMetaclass]
4482-
from typing import Any
4483-
M = None # type: Any
4484-
class A(metaclass=M):
4485-
y = 0
4486-
reveal_type(A.y) # N: Revealed type is "builtins.int"
4487-
A.x # E: "Type[A]" has no attribute "x"
4508+
reveal_type(A.x) # N: Revealed type is "Any"
44884509

44894510
[case testValidTypeAliasAsMetaclass]
44904511
from typing_extensions import TypeAlias

0 commit comments

Comments
 (0)