Skip to content

Commit

Permalink
Prevent unused-import for if t.TYPE_CHECKING
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Jun 6, 2022
1 parent 8dd278a commit b7d14fb
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 10 deletions.
5 changes: 5 additions & 0 deletions doc/whatsnew/2/2.14/full.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ 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.

Expand Down
36 changes: 28 additions & 8 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
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

Expand Down Expand Up @@ -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()
Expand Down
24 changes: 23 additions & 1 deletion tests/functional/u/unused/unused_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down Expand Up @@ -65,3 +68,22 @@ def blop(self):
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
6 changes: 5 additions & 1 deletion tests/functional/u/unused/unused_import.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit b7d14fb

Please sign in to comment.