mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Don't emit an "x has no attribute y" error if "y" was defined in __slots__

Open exhuma opened this issue 6 years ago • 6 comments

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.

exhuma avatar Nov 23 '18 10:11 exhuma

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?

rhocairn avatar Jan 22 '19 20:01 rhocairn

A possible workaround is:

from typing import TYPE_CHECKING

class Data:
    __slots__ = ('x', 'y', 'z')
    if TYPE_CHECKING:
        x: int
        y: int
        z: int

ilevkivskyi avatar Jan 22 '19 23:01 ilevkivskyi

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.

rhocairn avatar Jan 22 '19 23:01 rhocairn

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.

ilevkivskyi avatar Jan 22 '19 23:01 ilevkivskyi

Hi, can I please ask if there's been any development on this?

yashvardhannanavati avatar Aug 03 '22 02:08 yashvardhannanavati

@ilevkivskyi's workaround seems pretty good to me. Note you don't even need the if TYPE_CHECKING.

JelleZijlstra avatar Aug 04 '22 02:08 JelleZijlstra