-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Attribute with the same name as a class in type annotation #1775
Comments
There's a big code smell though, using the class name as the name for a variable containing instances of that class. I'm not sure if we should encourage that. Reminds me a bit of #1776 though. |
Same issue found with a method definition class A: pass
class B:
def a(self) -> A: ...
def A(self) -> A: ...
def b(self) -> A: ... gives
Also, the error is only raised after the line the name is redefined and then can't be used again. It is clearly a code smell, but it was found in stdlib while typing datetime.datetime.tzinfo which return a |
I'd introduce a type alias to work around the issue. For example:
|
I found other cases of this, even when the name is declared as an attribute. For example this: class Sentence:
def __init__(self):
self.subject = "Bob"
self.verb = "writes"
self.object = "a letter"
def __eq__(self, other: object) -> bool:
return isinstance(other, Sentence) and other.verb == self.verb This fails with (As a side note, this could get even more confusing with PEP526, you'll have a |
Ran into this one tonight in the ulid-py library after chasing my tail a bit. Example: @property
def bytes(self) -> bytes:
"""
Computes the bytes value of the underlying :class:`~memoryview`.
:return: Memory in bytes form
:rtype: :class:`~bytes`
"""
return self.memory.tobytes() Output: ⇒ mypy ulid
ulid/ulid.py:113: error: Invalid type "bytes" It may be code smell (property matching a builtin type name) but it's following the same pattern as the stdlib uuid.UUID class. I'll likely just define some type hint aliases to work-around the problem but it would be nice to see addressed if possible, or at the very least, a more "helpful" error message if this case is detectable vs. other "Invalid type" errors. |
I think that in this case mypy doesn't treat the scopes the same way as Python does at runtime, and the example @property # or without this
def bytes(self) -> bytes: should be supported. Though note that the following will be wrong at runtime: class C:
def bytes(self) -> bytes: ...
def frombytes(self, x: bytes): ... Here, |
@gvanrossum Thanks for the detailed explanation; defining aliases for those name clashes worked out. |
This works at least a little better now since mypy switched to the new semantic analyzer. Otherwise, the alias trick seems like a reasonable workaround. |
This issue should probably be re-open. This code works both on mypy and pyright (with the difference that mypy needs the from __future__ import annotations
from typing import TypeAlias
class C:
F:TypeAlias = bytes
def bytes(self) -> F:
return b'' But the following code causes mypy to complain about from __future__ import annotations
class C:
F = b'not a valid type'
def bytes(self) -> F:
return F()
class F:
... Looks like pyright starts by looking for annotation names in the global scope, then tries the locals (where mypy does the opposite). |
The behavior of mypy matches what happens at runtime, which is usually what we want: class C:
F = b'foo'
def f(self) -> F: ...
print(C.f.__annotations__) # {'return': b'foo'} |
@JukkaL note the future import. In this case the runtime behavior is not helping. |
I've open this issue on pyright's repo, and it looks like their conclusion is that mypy is inconsistent in this particular case. A clarification regarding pep 563 and it's implication on scoping rules of annotations would be good. |
This was reported by Agustin Barto:
I wonder if the body of
C
should be valid?The text was updated successfully, but these errors were encountered: