Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2074,12 +2074,17 @@ def get_declared_metaclass(
# Probably a name error - it is already handled elsewhere
return None, False
if isinstance(sym.node, Var) and isinstance(get_proper_type(sym.node.type), AnyType):
# 'Any' metaclass -- just ignore it.
#
# TODO: A better approach would be to record this information
# and assume that the type object supports arbitrary
# attributes, similar to an 'Any' base class.
return None, False
# Create a fake TypeInfo that fallbacks to `Any`, basically allowing
# all the attributes. Same thing as we do for `Any` base class.
any_info = self.make_empty_type_info(ClassDef(sym.node.name, Block([])))
any_info.fallback_to_any = True
any_info._fullname = sym.node.fullname
if self.options.disallow_subclassing_any:
self.fail(
f'Class cannot use "{any_info.fullname}" as a metaclass (has type "Any")',
metaclass_expr,
)
return Instance(any_info, []), False
if isinstance(sym.node, PlaceholderNode):
return None, True # defer later in the caller

Expand Down
36 changes: 27 additions & 9 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -4392,6 +4392,32 @@ def f(TB: Type[B]):
reveal_type(TB) # N: Revealed type is "Type[__main__.B]"
reveal_type(TB.x) # N: Revealed type is "builtins.int"

[case testMetaclassAsAny]
from typing import Any, ClassVar

MyAny: Any
class WithMeta(metaclass=MyAny):
x: ClassVar[int]

reveal_type(WithMeta.a) # N: Revealed type is "Any"
reveal_type(WithMeta.m) # N: Revealed type is "Any"
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a couple of tests to check/clarify that this only affects class objects, not instances of classes?

Suggested change
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
reveal_type(WithMeta().a) # E: "WithMeta" has no attribute "a" \
# N: Revealed type is "Any"
reveal_type(WithMeta().x) # N: Revealed type is "builtins.int"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!


[case testMetaclassAsAnyWithAFlag]
# flags: --config-file tmp/mypy.ini
Comment thread
sobolevn marked this conversation as resolved.
Outdated
from typing import Any, ClassVar

MyAny: Any
class WithMeta(metaclass=MyAny): # E: Class cannot use "__main__.MyAny" as a metaclass (has type "Any")
x: ClassVar[int]

reveal_type(WithMeta.a) # N: Revealed type is "Any"
reveal_type(WithMeta.m) # N: Revealed type is "Any"
reveal_type(WithMeta.x) # N: Revealed type is "builtins.int"
[file mypy.ini]
\[mypy]
disallow_subclassing_any = True
Comment thread
sobolevn marked this conversation as resolved.
Outdated

[case testMetaclassIterable]
from typing import Iterable, Iterator

Expand Down Expand Up @@ -4476,15 +4502,7 @@ from missing import M
class A(metaclass=M):
y = 0
reveal_type(A.y) # N: Revealed type is "builtins.int"
A.x # E: "Type[A]" has no attribute "x"

[case testAnyMetaclass]
from typing import Any
M = None # type: Any
class A(metaclass=M):
y = 0
reveal_type(A.y) # N: Revealed type is "builtins.int"
A.x # E: "Type[A]" has no attribute "x"
reveal_type(A.x) # N: Revealed type is "Any"

[case testValidTypeAliasAsMetaclass]
from typing_extensions import TypeAlias
Expand Down