Skip to content
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

False-positive E1101 (no-member) for writable file-like object #3234

Closed
tucked opened this issue Nov 7, 2019 · 11 comments
Closed

False-positive E1101 (no-member) for writable file-like object #3234

tucked opened this issue Nov 7, 2019 · 11 comments
Assignees
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code typing

Comments

@tucked
Copy link

tucked commented Nov 7, 2019

Steps to reproduce

"""sscce.py"""

class Context:
    def __enter__(self):
        return self
    def __exit__(self, type_, exc, traceback):
        return False

class Iterable(Context):
    def __iter__(self):
        return self
    def __next__(self):
        return self
    def write(self, x):
        print(x)

class Foo(Iterable):
    pass

with Foo() as foo:
    foo.write('something')

Note: Like #3233, this was found while using paramiko:

        with self._sftp.open(local_path, mode) as local_file:
            local_file.write(contents)  # triggers on write

local_file is a paramiko.SFTPFile which inherits from paramiko.BufferedFile which inherits paramiko.util.ClosingContextManager.

Current behavior

sscce.py:21:4: E1101: Instance of 'Context' has no 'write' member (no-member)

Expected behavior

(no error)

pylint --version output

pylint 2.4.3
astroid 2.3.3
Python 3.6.8 (default, Oct  7 2019, 12:59:55)
[GCC 8.3.0]
@simonvanderveldt
Copy link

Can confirm, just ran into this as well in our case we're using paramiko.Transport like this

with paramiko.Transport(...) as transport:
        transport.connect(...)

And we get this linting error

app/lambda_function.py:98:8: E1101: Instance of 'ClosingContextManager' has no 'connect' member (no-member)

I'm not even sure how it gets to the ClosingContextManager since the Transport class itself has a connect() member
https://github.com/paramiko/paramiko/blob/2.6.0/paramiko/transport.py#L1222

@PCManticore
Copy link
Contributor

Thanks for the example, folks, I can confirm the bug.

@PCManticore PCManticore self-assigned this Nov 8, 2019
@actionless
Copy link

actionless commented Nov 25, 2019

i wonder, if it's the same issue or not:

from typing import Any, List, Optional


NOT_FOUND_ATOM = object()


class AnnotatedDataType():

    def _key_not_exists(self, key):
        return getattr(self, key, NOT_FOUND_ATOM) is NOT_FOUND_ATOM

    def __init__(self, **kwargs) -> None:
        for key, value in kwargs.items():
            setattr(self, key, value)
        for key in self.__annotations__:  # pylint: disable=no-member
            if self._key_not_exists(key):
                raise TypeError(
                    f"'{self.__class__.__name__}' does "
                    f"not have required attribute '{key}' set"
                )

    def __setattr__(self, key: str, value: Any) -> None:
        if (
                not getattr(self, "__annotations__", None) or
                self.__annotations__.get(key, NOT_FOUND_ATOM) is NOT_FOUND_ATOM  # pylint: disable=no-member
        ) and self._key_not_exists(key):
            raise TypeError(
                f"'{self.__class__.__name__}' does "
                f"not have attribute '{key}'"
            )
        super().__setattr__(key, value)




class PackagesNotFound(AnnotatedDataType, Exception):
    packages: List[str]
    wanted_by: Optional[List[str]] = None

    def __init__(self, packages: List[str], wanted_by: Optional[List[str]] = None) -> None:
        AnnotatedDataType.__init__(self, packages=packages, wanted_by=wanted_by)
        message = ', '.join(packages)
        if wanted_by:
            message += f" wanted by {', '.join(wanted_by)}"
        Exception.__init__(self, message)


class PackagesNotFoundInRepo(PackagesNotFound):
    pass


def main():
    try:
        raise PackagesNotFoundInRepo(packages=['a', 'b', 'c'])
    except PackagesNotFound as exc:
        print(exc.packages)
        #      ^ here pylint is NOT complaining
    try:
        raise PackagesNotFoundInRepo(packages=['a', 'b', 'c'])
    except PackagesNotFoundInRepo as exc:
        print(exc.packages)
        #     ^ [pylint] no-member: Instance of 'PackagesNotFoundInRepo' has no 'packages' member


if __name__ == "__main__":
    main()

inducer pushed a commit to inducer/dagrt that referenced this issue Feb 6, 2020
@oshotton
Copy link

I think there is the same issue with .__annotations__. The example below is with a TypedDict but I imagine it is the same for all uses of .__annotations__.

from typing import TypedDict


class Example(TypedDict):
    string: str
    integer: int
    boolean: bool


print(Example.__annotations__)

Outputs:
{'string': <class 'str'>, 'integer': <class 'int'>, 'boolean': <class 'bool'>}

But pylint complains:
scratch.py:10:6: E1101: Class 'Example' has no '__annotations__' member (no-member)

@Pierre-Sassoulas Pierre-Sassoulas added the False Positive 🦟 A message is emitted but nothing is wrong with the code label Aug 11, 2021
@actionless
Copy link

i slightly updated my previous message, because the main point was not the annotations (which also indeed are not working there)

@fenuks
Copy link

fenuks commented Aug 31, 2021

As for __annotations__, no-member warning is indeed also issued for other kind of typed things in typing module, e.g. NamedTuple:

from typing import NamedTuple


class Foo(NamedTuple):
    foobar: str

print(Foo.__annotations__)
test.py:7:6: E1101: Class 'Foo' has no '__annotations__' member (no-member)
$ pylint --version
pylint 2.10.2
astroid 2.7.2
Python 3.9.6 (default, Aug 23 2021, 11:20:01) 

@cdce8p
Copy link
Member

cdce8p commented Oct 17, 2021

This issues seems to be fixed when testing with the latest pylint version.
If you still encounter issues, please report back and I'll reopen it.

@cdce8p cdce8p closed this as completed Oct 17, 2021
@fenuks
Copy link

fenuks commented Oct 17, 2021

Latest tagged version? With

$ pylint --version
pylint 2.11.1
astroid 2.8.2
Python 3.9.7 (default, Oct 10 2021, 15:13:22) 
[GCC 11.1.0]

for the example I posted above problem still persists.

@cdce8p cdce8p added the typing label Oct 17, 2021
@cdce8p
Copy link
Member

cdce8p commented Oct 17, 2021

@fenuks I'd tested it with Python 3.10 which works.
However, you're right at least __annotations__ is still broken for Python < 3.10

I can't reproduce the original issue with Context.write though. Even not with Python 3.8

@tucked
Copy link
Author

tucked commented Oct 26, 2021

Just like #3233, the original issue (not sure exactly where __annotations__ ties in) appears to have been fixed in 2.5.0, likely because astroid was upgraded:

python3.6 -m venv venv
venv/bin/python -m pip install pylint==2.4.4
venv/bin/pylint sscce.py  # repro
venv/bin/python -m pip install astroid~=2.4.0
venv/bin/pylint sscce.py  # NO repro

@Pierre-Sassoulas
Copy link
Member

Closing as the main issue was fixed, and I opened #7126 for the one that appeared along the way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code typing
Projects
None yet
Development

No branches or pull requests

8 participants