Skip to content

Commit 6a16625

Browse files
authored
Fix cyclic import with TYPE_CHECKING (#4703)
* Fix cyclic-import with TYPE_CHECKING * Use new astroid helper method
1 parent 6950464 commit 6a16625

File tree

7 files changed

+34
-3
lines changed

7 files changed

+34
-3
lines changed

ChangeLog

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ Release date: TBA
5252

5353
Closes #4664
5454

55+
* Don't emit ``cyclic-import`` message if import is guarded by ``typing.TYPE_CHECKING``.
56+
57+
Closes #3525
58+
5559
* Clarify documentation for consider-using-from-import
5660

5761
* Don't emit ``unreachable`` warning for empty generator functions

pylint/checkers/imports.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,9 @@ def _get_imported_module(self, importnode, modname):
813813
self.add_message("import-error", args=repr(dotted_modname), node=importnode)
814814
return None
815815

816-
def _add_imported_module(self, node, importedmodname):
816+
def _add_imported_module(
817+
self, node: Union[astroid.Import, astroid.ImportFrom], importedmodname: str
818+
) -> None:
817819
"""notify an imported module, used to analyze dependencies"""
818820
module_file = node.root().file
819821
context_name = node.root().name
@@ -826,6 +828,10 @@ def _add_imported_module(self, node, importedmodname):
826828
except ImportError:
827829
pass
828830

831+
in_type_checking_block = (
832+
isinstance(node.parent, astroid.If) and node.parent.is_typing_guard()
833+
)
834+
829835
if context_name == importedmodname:
830836
self.add_message("import-self", node=node)
831837

@@ -845,7 +851,10 @@ def _add_imported_module(self, node, importedmodname):
845851

846852
# update import graph
847853
self.import_graph[context_name].add(importedmodname)
848-
if not self.linter.is_message_enabled("cyclic-import", line=node.lineno):
854+
if (
855+
not self.linter.is_message_enabled("cyclic-import", line=node.lineno)
856+
or in_type_checking_block
857+
):
849858
self._excluded_edges[context_name].add(importedmodname)
850859

851860
def _check_preferred_module(self, node, mod_path):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# https://github.com/PyCQA/pylint/issues/3525
2+
# `cyclic-import` should not be emitted if one import
3+
# is guarded by `typing.TYPE_CHECKING`

tests/input/func_noerror_cycle/a.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# pylint: disable=missing-docstring
2+
from typing import List
3+
4+
from .b import var
5+
6+
LstT = List[int]
7+
8+
print(var)

tests/input/func_noerror_cycle/b.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# pylint: disable=missing-docstring
2+
from typing import TYPE_CHECKING
3+
4+
if TYPE_CHECKING:
5+
from .a import LstT
6+
7+
var: "LstT" = [1, 2]

tests/messages/func_noerror_cycle.txt

Whitespace-only changes.

tests/test_func.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def gen_tests(filter_rgx):
121121
tests.append((module_file, messages_file, dependencies))
122122
if UPDATE_FILE.exists():
123123
return tests
124-
assert len(tests) < 12, "Please do not add new test cases here." + "\n".join(
124+
assert len(tests) < 13, "Please do not add new test cases here." + "\n".join(
125125
str(k) for k in tests if not k[2]
126126
)
127127
return tests

0 commit comments

Comments
 (0)