diff options
author | Jacob Walls <jacobtylerwalls@gmail.com> | 2022-06-01 11:29:04 -0400 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2022-06-06 22:27:46 +0200 |
commit | 7e44744cf4b72da53ebeddfdd1bc480bf9ec6d43 (patch) | |
tree | c8aa0a11b39201942cfb2f7877f22c1b4a4d6fbf | |
parent | ac6615a1570b53f4e08b45cb9baf509b7b810763 (diff) | |
download | pylint-git-7e44744cf4b72da53ebeddfdd1bc480bf9ec6d43.tar.gz |
Prevent `unused-import` for `if t.TYPE_CHECKING`
-rw-r--r-- | doc/whatsnew/2/2.14/full.rst | 10 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 36 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_import.py | 24 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_import.txt | 6 |
4 files changed, 66 insertions, 10 deletions
diff --git a/doc/whatsnew/2/2.14/full.rst b/doc/whatsnew/2/2.14/full.rst index 056c71331..6b66ba49b 100644 --- a/doc/whatsnew/2/2.14/full.rst +++ b/doc/whatsnew/2/2.14/full.rst @@ -22,6 +22,16 @@ Release date: TBA Closes #6802 +* Fixed false positives for ``unused-import`` when aliasing ``typing`` e.g. as ``t`` + and guarding imports under ``t.TYPE_CHECKING``. + + Closes #3846 + +* Fixed a false positive regression in 2.13 for ``used-before-assignment`` where it is safe to rely + on a name defined only in an ``except`` block because the ``else`` block returned. + + Closes #6790 + * Fixed the use of abbreviations for some special options on the command line. Closes #6810 diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 8ae5f354b..ce46dfcbb 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -23,8 +23,6 @@ from astroid import TooManyLevelsError, nodes from astroid.context import InferenceContext from astroid.exceptions import AstroidError -from pylint.constants import TYPING_TYPE_CHECKS_GUARDS - if TYPE_CHECKING: from pylint.checkers import BaseChecker @@ -1778,12 +1776,34 @@ def get_node_first_ancestor_of_type_and_its_child( def in_type_checking_block(node: nodes.NodeNG) -> bool: - """Check if a node is guarded by a TYPE_CHECKS guard.""" - return any( - isinstance(ancestor, nodes.If) - and ancestor.test.as_string() in TYPING_TYPE_CHECKS_GUARDS - for ancestor in node.node_ancestors() - ) + """Check if a node is guarded by a TYPE_CHECKING guard.""" + for ancestor in node.node_ancestors(): + if not isinstance(ancestor, nodes.If): + continue + if isinstance(ancestor.test, nodes.Name): + if ancestor.test.name != "TYPE_CHECKING": + continue + maybe_import_from = ancestor.test.lookup(ancestor.test.name)[1][0] + if ( + isinstance(maybe_import_from, nodes.ImportFrom) + and maybe_import_from.modname == "typing" + ): + return True + inferred = safe_infer(ancestor.test) + if isinstance(inferred, nodes.Const) and inferred.value is False: + return True + elif isinstance(ancestor.test, nodes.Attribute): + if ancestor.test.attrname != "TYPE_CHECKING": + continue + inferred_module = safe_infer(ancestor.test.expr) + if ( + isinstance(inferred_module, nodes.Module) + and inferred_module.name == "typing" + and inferred_module.pytype() == "builtins.module" + ): + return True + + return False @lru_cache() diff --git a/tests/functional/u/unused/unused_import.py b/tests/functional/u/unused/unused_import.py index 0f0f09fff..143ee7cf3 100644 --- a/tests/functional/u/unused/unused_import.py +++ b/tests/functional/u/unused/unused_import.py @@ -17,15 +17,18 @@ class SomeClass(object): SomeOtherName = SomeOtherName from never import __all__ -# pylint: disable=wrong-import-order,ungrouped-imports +# pylint: disable=wrong-import-order,ungrouped-imports,reimported import typing from typing import TYPE_CHECKING +import typing as t if typing.TYPE_CHECKING: import collections if TYPE_CHECKING: import itertools +if t.TYPE_CHECKING: + import xml def get_ordered_dict() -> 'collections.OrderedDict': @@ -65,3 +68,22 @@ class NonRegr(object): if TYPE_CHECKING: if sys.version_info >= (3, 6, 2): from typing import NoReturn + +# Pathological cases +from io import TYPE_CHECKING # pylint: disable=no-name-in-module +import trace as t +import astroid as typing +TYPE_CHECKING = "red herring" + +if TYPE_CHECKING: + import unittest # [unused-import] +if t.TYPE_CHECKING: # pylint: disable=no-member + import uuid # [unused-import] +if typing.TYPE_CHECKING: # pylint: disable=no-member + import warnings # [unused-import] +if typing.TYPE_CHECKING_WITH_MAGIC: # pylint: disable=no-member + import compileall # [unused-import] + +TYPE_CHECKING = False +if TYPE_CHECKING: + import zoneinfo diff --git a/tests/functional/u/unused/unused_import.txt b/tests/functional/u/unused/unused_import.txt index 1003dafb7..059405388 100644 --- a/tests/functional/u/unused/unused_import.txt +++ b/tests/functional/u/unused/unused_import.txt @@ -7,4 +7,8 @@ unused-import:9:0:9:51::Unused OrderedDict imported from collections:UNDEFINED unused-import:9:0:9:51::Unused deque imported from collections:UNDEFINED unused-import:10:0:10:22::Unused import re:UNDEFINED unused-import:13:0:13:40::Unused SomeOtherName imported from fake:UNDEFINED -unused-import:45:0:45:9::Unused import os:UNDEFINED +unused-import:48:0:48:9::Unused import os:UNDEFINED +unused-import:79:4:79:19::Unused import unittest:UNDEFINED +unused-import:81:4:81:15::Unused import uuid:UNDEFINED +unused-import:83:4:83:19::Unused import warnings:UNDEFINED +unused-import:85:4:85:21::Unused import compileall:UNDEFINED |