Skip to content

Classmethods on typing.NamedTuple's don't recognize class attributes (no-member) #2688

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

Closed
nicktimko opened this issue Jan 10, 2019 · 7 comments
Labels
Bug 🪲 Good first issue Friendly and approachable by new contributors Hacktoberfest Help wanted 🙏 Outside help would be appreciated, good for new contributors Needs astroid Brain 🧠 Needs a brain tip in astroid (then an astroid upgrade) typing

Comments

@nicktimko
Copy link

Class attributes on typing.NamedTuples don't seem to be picked up, so pylint thinks they're missing and throws a no-member error. Sounds related to #1628 which was about methods on them...if you want to point me at some stuff, I could take a look at trying to tweak it. I'm still kinda lost in the pylint/astroid code, so not super productive yet...

Steps to reproduce

import typing

class Spam:
    EGGS = 1

    @classmethod
    def spam(cls):
        return cls.EGGS  # no errors

class Spamalot(typing.NamedTuple):
    name: str

    EGGS = 1

    @classmethod
    def spam(cls):
        return cls.EGGS  # Class 'Spamalot' has no 'EGGS' member (no-member)

    def title(self):
        return self.EGGS  # no errors here either.

assert Spam.spam() == Spamalot.spam()

Current behavior

$ pylint --version
pylint 2.2.2
astroid 2.1.0
Python 3.6.6 (default, Sep 17 2018, 13:34:13)
[GCC 5.4.0 20160609]
$ pylint ntclassmethod.py
************* Module ntclassmethod
ntclassmethod.py:19:15: E1101: Class 'Spamalot' has no 'EGGS' member (no-member) 

------------------------------------------------------------------
Your code has been rated at 6.15/10 (previous run: 6.15/10, +0.00)

$ python ntclassmethod.py
$

Expected behavior

No pylint errors

pylint --version output

@nicktimko
Copy link
Author

Hrm, I think the classmethod thing is a bit of a red herring and/or too-specific. Externally referencing the class attribute also raises an error:

import typing

class Spamalot(typing.NamedTuple):
    name: str
    EGGS = 1

    def info(self):
        print(self.EGGS)

print(Spamalot.EGGS)  # E1101: Class 'Spamalot' has no 'EGGS' member (no-member)
Spamalot("frank").info()

@PCManticore
Copy link
Contributor

@nicktimko I think this bug comes from our lossy understanding of typing.NamedTuple. Everything happens in astroid in this function https://github.com/PyCQA/astroid/blob/fd41070d76f323f089a120c72fb9b0d87e7f4a20/astroid/brain/brain_namedtuple_enum.py#L364. I think we need to change that conversion from a typing.NamedTuple
class to a namedtuple class, by returning something along the following lines:

from collections import namedtuple
class {typename}(namedtuple({typename!r}, {fields!r})):
     # Now add here the methods / attributes that we identified in the original typing.NamedTuple class
     pass

Next the inference engine should know what attributes this class has. Let me know if this makes sense, happy to help with a review or more indications on how to fix this.

@PCManticore PCManticore added Bug 🪲 Good first issue Friendly and approachable by new contributors Needs astroid Brain 🧠 Needs a brain tip in astroid (then an astroid upgrade) labels Jan 13, 2019
@Thf772
Copy link

Thf772 commented Jun 14, 2019

Hello,

Adding to the problem: I have the same result in non-class methods:

import enum, typing

class TestNamedTuple(typing.NamedTuple):
    member : str

class TestEnum(enum.Enum):
    value : TestNamedTuple

    TEST_VALUE = TestNamedTuple("test")

    def test_method(self):
        return self.value.member
$ pylint .\enum_test.py -E
************* Module enum_test
enum_test.py:14:9: E1101: Instance of 'value' has no 'member' member (no-member)

This behavior is observed with both typing.NamedTuple and collections.namedtuple. I have also tested with --load-plugins=pylint_enums with the same results.


Using Python 3.7.2 on Windows 10, same results with Python 3.7.3 in the WSL.

$ pip freeze | grep 'pylint\|astroid'
astroid==2.2.5
pylint==2.3.1
pylint-enums==0.0.5

@Pierre-Sassoulas Pierre-Sassoulas added the Help wanted 🙏 Outside help would be appreciated, good for new contributors label Mar 2, 2021
@whoburg
Copy link

whoburg commented Apr 25, 2021

possibly related: the same no-member behavior occurs when attempting to access _asdict, which is a valid NamedTuple method, in a subclass of NamedTuple:

from typing import NamedTuple


class Test(NamedTuple):
    a: int
    b: str

    def foo(self):
        return self._asdict()  # E1101: Instance of 'Test' has no '_asdict' member (no-member)
❯ pylint --version
pylint 2.8.0
astroid 2.5.5
Python 3.8.9 (default, Apr  3 2021, 01:50:09)
[Clang 12.0.0 (clang-1200.0.32.29)]

@clavedeluna
Copy link
Contributor

This issue has been fixed somewhere along!

@mbyrnepr2
Copy link
Member

While the original example no longer emits no-member, the last two examples in the comments are still emitting no-member so I think we need to keep this open or create new issues for those; otherwise we lose valid reports.

@Pierre-Sassoulas
Copy link
Member

Opened #7891

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🪲 Good first issue Friendly and approachable by new contributors Hacktoberfest Help wanted 🙏 Outside help would be appreciated, good for new contributors Needs astroid Brain 🧠 Needs a brain tip in astroid (then an astroid upgrade) typing
Projects
None yet
Development

No branches or pull requests

8 participants