mypy
mypy copied to clipboard
Don't emit an "x has no attribute y" error if "y" was defined in __slots__
Python version: 3.5.2 mypy version: 0.630 Bug of Feature request: Feature Request
When defining a class with __slots__
mypy complains that the instance has no attribute, even if it was defined in __slots__
.
Consider the following code:
class Foo:
__slots__ = ['a']
instance = Foo()
instance.a = 10 # <-- False positive ("Foo" has no attribute "a")
instance.b = 20 # <-- True positive
In this case I would expect mypy
to accept assigning something to a
(using type Any
), but still complain about assigning to b
.
The docs about __slots__
state that any iterable can be assigned, and contains the following section:
Any non-string iterable may be assigned to slots. Mappings may also be used; however, in the future, special meaning may be assigned to the values corresponding to each key.
So it may also be interesting to allow typing of the entries in __slots__
, for example:
class Bar:
__slots__ = {'a': int}
instance2 = Bar()
instance2.a = 10
instance2.a = 'foo' # <-- I would expect an error here
The docs about __slots__
don't say anything about ordering of the items. So this should be safe even in older version of Python where dicts did not keep ordering yet.
I also came across #1211 and #1325 which both mention __slots__
but are different enough that this should be a separate issue I think.
Any movement on this? I have some classes using __slots__
and it'd be nice to not get these false positives anymore. What workarounds have folks used in the meantime?
A possible workaround is:
from typing import TYPE_CHECKING
class Data:
__slots__ = ('x', 'y', 'z')
if TYPE_CHECKING:
x: int
y: int
z: int
Thanks @ilevkivskyi . I thought about doing that, but unfortunately I'm stuck on python 3.4 for this particular project, and 3.4 doesn't support class annotations. I'm currently setting them in __init__
as a workaround, but interested in alternatives.
Another option on 3.4 is to use type comments:
class Data:
__slots__ = ('x', 'y', 'z')
if TYPE_CHECKING:
x = None # type: int
y = None # type: int
z = None # type: int
but I am not sure it is better.
Hi, can I please ask if there's been any development on this?
@ilevkivskyi's workaround seems pretty good to me. Note you don't even need the if TYPE_CHECKING
.