pylint-dev / pylint

It's not just a linter that annoys you!
https://pylint.readthedocs.io/en/latest/
GNU General Public License v2.0
5.25k stars 1.12k forks source link

Incorrect report "unused-import" when assigning to class attribute with the same name as the imported module. #4993

Open DerBiasto opened 3 years ago

DerBiasto commented 3 years ago

Bug description

Pylint incorrectly reports that a module was imported but not used, when the module is only used in a type annotation of a class that has a @property decorated function with the same name as the imported module.


import gettext

class C:
    def __init__(self, translation: gettext.NullTranslations) -> None:
        self.translation = translation

    @property
    def gettext(self) -> None:
        return None

If the gettext module is used elsewhere, the error won't be reported, e.g.:

import gettext

class C:
    def __init__(self, translation: gettext.NullTranslations) -> None:
        self.translation: gettext.NullTranslations = translation

    @property
    def gettext(self) -> None:
        return None

If the property is in a different class, the error won't be reported either, e.g.:

import gettext

class C:
    def __init__(self, translation: gettext.NullTranslations) -> None:
        self.translation = translation

class B:
    @property
    def gettext(self) -> None:
        return None

This is not specific to the gettext module either, e.g.:

import decimal

class C:
    def __init__(self, d: decimal.Decimal) -> None:
        pass

    @property
    def decimal(self) -> None:
        return None

Configuration

No response

Command used

pylint a.py

Pylint output

************* Module a
a.py:1:0: C0114: Missing module docstring (missing-module-docstring)
a.py:4:0: C0103: Class name "C" doesn't conform to PascalCase naming style (invalid-name)
a.py:4:0: C0115: Missing class docstring (missing-class-docstring)
a.py:9:4: C0116: Missing function or method docstring (missing-function-docstring)
a.py:4:0: R0903: Too few public methods (1/2) (too-few-public-methods)
a.py:1:0: W0611: Unused import gettext (unused-import)

Expected behavior

The error W0611 should not be reported, since the gettext module is used.

Pylint version

pylint 2.7.2
astroid 2.5.1
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110]

OS / Environment

No response

Additional dependencies

No response

JohaJung commented 3 years ago

I tested that this also happens with the latest version from pip:

pylint 2.10.2
astroid 2.7.3
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110]
jacobtylerwalls commented 2 years ago

This affects any class attribute having the same name as the imported module, not just @property, so I suggest re-naming the issue.

import uuid

class MyClass:
    uuid = uuid.uuid4()
% python3 -m pylint uuid_test.py --disable=all --enable=unused-import
************* Module uuid_test
uuid_test.py:1:0: W0611: Unused import uuid (unused-import)

------------------------------------------------------------------
Your code has been rated at 6.67/10 (previous run: 6.67/10, +0.00)
teake commented 2 years ago

git-bisect tells me this bug was introduced in ea13058b9fde38698515c93bf67cc6018ed0064e. I've briefly tried to see if there was an easy fix, but that code has been refactored since and, unfortunately, I could make heads or tails out of it.

Pierre-Sassoulas commented 2 years ago

Seeing the diff for the commit that introduced the bug it look like the review of existing @check_message as suggested in #6060 by @DudeNr33 would be fruitful.

DudeNr33 commented 2 years ago

@teake Thanks for taking time to investigate. Are you sure that this commit introduced the bug? I can reproduce it when I roll back to the commit before that (c5faab4caa77c4f5b16f9a411c79c5b7a2e98753). I can also reproduce it on the current main branch, where the @check_messages decorator has been removed completely for this method.

teake commented 2 years ago

@DudeNr33 Sorry, you're right, I was a bit too hasty. I bisected not against OPs code snippet, but mine, assuming the underlying issue was identical (evidently not). My code snippet was:

from other import x, y  # raises unused-import (W0611) for x but not for y

class MyClass:
    x = float(x)
    y = y

This snippet will start raising a false positive at ea13058b9fde38698515c93bf67cc6018ed0064e when invoked as follows:

$ python -m pylint --disable=all --enable=W0611 <path_to_file>

But when invoked as

$ python -m pylint <path_to_file>

no false positive is raised, even though unused-import (W0611) is enabled by default.

As for @DerBiasto's code snippet, I checked that the unwanted behaviour was already present in PyLint 1.7.0. So it seems the two are similar but unrelated.

DanielNoord commented 2 years ago

@teake Could you open a new issue for your snippet? That does seem related to check_messages indeed 👍

teake commented 2 years ago

Done!

Pierre-Sassoulas commented 2 years ago

We merged #6060 but I can still reproduce this.