Skip to content
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

[red-knot] Either support metaclasses that are not subclasses of type, or emit a diagnostic when encountering them #14208

Open
AlexWaygood opened this issue Nov 8, 2024 · 2 comments · May be fixed by #15138
Labels
red-knot Multi-file analysis & type inference

Comments

@AlexWaygood
Copy link
Member

AlexWaygood commented Nov 8, 2024

Believe it or not, Python accepts arbitrary callables as metaclasses in class statements -- and those callables don't have to return instances of type. As long as the callable accepts the same arguments as type.__new__, it's an acceptable metaclass; the binding created by the class statement is set to whatever calling the metaclass returns:

>>> def returns_int(*args: object) -> int:
...     return 42
...     
>>> class A(metaclass=returns_int): ...
... 
>>> A
42

Currently we don't understand this in red-knot: we think that the class A statement constitutes a class definition, when it does not. Mypy "solves" this problem by emitting a diagnostic saying that a dynamic metaclass like this isn't supported. Pyright doesn't emit a diagnostic, and just gets it wrong.

I think mypy's "solution" here is acceptable (metaclasses that are not subclasses of type are really very rare). But it also might not be too much work for us to just add an accurate understanding of the runtime semantics here, and infer that the class A statement binds the A symbol to an instance of int rather than a newly created class.

@AlexWaygood AlexWaygood added the red-knot Multi-file analysis & type inference label Nov 8, 2024
@AlexWaygood AlexWaygood changed the title [red-knot] Either support metaclasses that are subclasses of type, or emit a diagnostic when encountering them [red-knot] Either support metaclasses that are not subclasses of type, or emit a diagnostic when encountering them Nov 8, 2024
@InSyncWithFoo
Copy link
Contributor

Pyright's behavior seems to be a deliberate design choice:

If a custom metaclass is present, pyright evaluates its __call__ method to determine whether it returns an instance of the class. If not, it assumes that the metaclass has custom behavior that overrides type.__call__. Likewise, if a class provides a __new__ method that returns a type other than the class being constructed (or a child class thereof), it assumes that __init__ will not be called.

Comparison with Mypy § Constructor Calls

@AlexWaygood
Copy link
Member Author

Pyright's behavior seems to be a deliberate design choice:

Thanks, that's interesting! I still dislike pyright's behaviour here, though, even if it's deliberate. There's lots of potentially invalid metaclasses that pyright simply doesn't detect, and that I think it would be quite easily to statically detect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants