Pylint: False no-member positive on defaultless instance variables on subclasses

Created on 6 Oct 2019  路  9Comments  路  Source: PyCQA/pylint

t.py:

class A:
    myfield: int

class B(A):
    pass

a = A()
print(a.myfield)

b = B()
print(b.myfield)
# pylint 2.4.2, astroid 2.3.1
$ pylint t.py | grep no-member
t.py:11:6: E1101: Instance of 'B' has no 'myfield' member (no-member)

Pylint doesn't appear to "see" the subclass myfield member declared in the parent class (reports the error against B), but does see it in the parent class (doesn't report against A).

(Technically I suppose the member isn't actually there in the parent class either as it's an instance variable without a default [1], and I believe they kind of spring to life only when actually set to a value which doesn't happen in this example -- for this reason the example code actually raises when run. Pylint seems to "know" that it's (or will be) there nevertheless which I think is great, but it should also know that it's there in the subclass.
[1] https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations)

enhancement topic-inference

Most helpful comment

@PCManticore Is there any movement on resolving the scenario in the original issue? We are running into this issue on a daily basis. 馃檪

All 9 comments

Yeah, I think pylint should accept subclasses as well and maybe metaclasses. The reason we don't emit these is that variable annotations can be used to specify attributes that might exist at runtime somehow.

You get the same error if A contains a method that refers to the declaration:

class A:
    myfield: int

    def get_myfield(self):
        return self.myfield # no-member

That is right, because myfield is static and can only be accessed with A.myfield

That is right, because myfield is static and can only be accessed with A.myfield

I think you are mistaken on two accounts here:

  • You can indeed access static members through instances, so accessing A().myfield would be ok.
  • Apart from being valid python, it is also a common way of telling static analyzers (like mypy) that instances of this class have a certain member, that might otherwise go undetected. Note, that if the author wanted to create an actual static member, they would have just defined its value there, instead of just declaring its type.

Yes, that is right. That was actually an instance variable annotation. Class variables are supposed to use the ClassVar annotation:

class A:
    myclassvar: ClassVar[int]

I do think that the chosen syntax has resulted in some confusion, but it is what it is.

As a workaround, you can assign the instance variable in a if False block in __init__:

class A:
    myvar: int

    def __init__(self):
        if False # pylint: disable=using-constant-test
            self.myvar = 42

Ick. But at least it saves you from having to add pylint comments anywhere the
instance variable is accessed.

If anyone knows a better workaround, I would love to hear it.

@PCManticore Is there any movement on resolving the scenario in the original issue? We are running into this issue on a daily basis. 馃檪

This issue causes us to either use improper typing and assign a None default and constantly make sure that it is asserted to satisfy type checking, or ignore about every line to either satisfy type checking or pylint.

It is rather sad, as it really degrades the whole goal of these tools in the first place. It has been a frustrating experience. Really hope that this can be picked up by someone 馃槩

I opened #4194 to fix this issue

Was this page helpful?
0 / 5 - 0 ratings

Related issues

PCManticore picture PCManticore  路  3Comments

mrginglymus picture mrginglymus  路  3Comments

PCManticore picture PCManticore  路  3Comments

Hubro picture Hubro  路  3Comments

DGalt picture DGalt  路  3Comments