Skip to content

Commit ce840fb

Browse files
committed
Add linting for imports under if TYPE_CHECKING: block
1 parent 77de645 commit ce840fb

27 files changed

+430
-5
lines changed

README.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ Limitations
7373
-----------
7474

7575
Currently these checks are limited to module scope imports only.
76-
Conditional imports in module scope will also be ignored.
76+
Conditional imports in module scope will be ignored except imports
77+
under ```if TYPE_CHECKING:``` block.
7778

7879
Classification of an imported module is achieved by checking the
7980
module against a stdlib list and then if there is no match against the

flake8_import_order/__init__.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
ClassifiedImport = namedtuple(
1919
'ClassifiedImport',
20-
['type', 'is_from', 'modules', 'names', 'lineno', 'level', 'package'],
20+
['type', 'is_from', 'modules', 'names', 'lineno', 'level', 'package',
21+
'type_checking'],
2122
)
2223
NewLine = namedtuple('NewLine', ['lineno'])
2324

@@ -70,8 +71,13 @@ def __init__(self, application_import_names, application_package_names):
7071
self.application_import_names = frozenset(application_import_names)
7172
self.application_package_names = frozenset(application_package_names)
7273

74+
def generic_visit(self, node):
75+
for child in ast.iter_child_nodes(node):
76+
child.parent = node
77+
return super().generic_visit(node)
78+
7379
def visit_Import(self, node): # noqa: N802
74-
if node.col_offset == 0:
80+
if node.col_offset == 0 or self._type_checking_import(node):
7581
modules = [alias.name for alias in node.names]
7682
types_ = {self._classify_type(module) for module in modules}
7783
if len(types_) == 1:
@@ -81,11 +87,12 @@ def visit_Import(self, node): # noqa: N802
8187
classified_import = ClassifiedImport(
8288
type_, False, modules, [], node.lineno, 0,
8389
root_package_name(modules[0]),
90+
self._type_checking_import(node),
8491
)
8592
self.imports.append(classified_import)
8693

8794
def visit_ImportFrom(self, node): # noqa: N802
88-
if node.col_offset == 0:
95+
if node.col_offset == 0 or self._type_checking_import(node):
8996
module = node.module or ''
9097
if node.level > 0:
9198
type_ = ImportType.APPLICATION_RELATIVE
@@ -96,9 +103,16 @@ def visit_ImportFrom(self, node): # noqa: N802
96103
type_, True, [module], names,
97104
node.lineno, node.level,
98105
root_package_name(module),
106+
self._type_checking_import(node),
99107
)
100108
self.imports.append(classified_import)
101109

110+
def _type_checking_import(self, node):
111+
return (
112+
isinstance(node.parent, ast.If)
113+
and node.parent.test.id == "TYPE_CHECKING"
114+
)
115+
102116
def _classify_type(self, module):
103117
package_names = get_package_names(module)
104118

flake8_import_order/styles.py

+15
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ def check(self):
3737
def _check(self, previous_import, previous, current_import):
3838
yield from self._check_I666(current_import)
3939
yield from self._check_I101(current_import)
40+
if (
41+
previous_import is not None
42+
and not previous_import.type_checking
43+
and current_import.type_checking
44+
):
45+
yield from self._check_I300(previous_import, current_import)
46+
previous_import = None
4047
if previous_import is not None:
4148
yield from self._check_I100(previous_import, current_import)
4249
yield from self._check_I201(previous_import, previous, current_import)
@@ -61,6 +68,14 @@ def _check_I101(self, current_import): # noqa: N802
6168
"Should be {}".format(corrected),
6269
)
6370

71+
def _check_I300(self, previous_import, current_import): # noqa: N802
72+
if current_import.lineno - previous_import.lineno != 3:
73+
yield Error(
74+
current_import.lineno,
75+
'I300',
76+
"TYPE_CHECKING block should have one newline above.",
77+
)
78+
6479
def _check_I100(self, previous_import, current_import): # noqa: N802
6580
previous_key = self.import_key(previous_import)
6681
current_key = self.import_key(current_import)

tests/test_cases/additional_newline.py

+25
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,28 @@
2222
from . import A
2323

2424
from . import B # I202
25+
26+
if TYPE_CHECKING:
27+
import ast
28+
# This comment should not trigger a I202 (not a newline)
29+
import os
30+
31+
import signal # I202
32+
33+
import X
34+
from X import B, b, \
35+
C, d
36+
37+
from Y import A # I202
38+
from Y import (
39+
B, b,
40+
C, d,
41+
)
42+
from Z import A
43+
44+
import flake8_import_order
45+
46+
import tests # I202
47+
from . import A
48+
49+
from . import B # I202

tests/test_cases/additional_newline_cryptography.py

+17
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,20 @@
1414
from . import A
1515

1616
from . import B # I202
17+
18+
if TYPE_CHECKING:
19+
import ast
20+
21+
import signal # I202
22+
23+
import X
24+
25+
import Y
26+
27+
import flake8_import_order
28+
29+
import tests
30+
31+
from . import A
32+
33+
from . import B # I202

tests/test_cases/additional_newline_edited.py

+26
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,29 @@
2323
from . import A
2424

2525
from . import B # I202
26+
27+
if TYPE_CHECKING:
28+
import ast
29+
# This comment should not trigger a I202 (not a newline)
30+
import os
31+
32+
import signal # I202
33+
34+
import X
35+
from X import B, b, \
36+
C, d
37+
38+
from Y import A # I202
39+
from Y import (
40+
B, b,
41+
C, d,
42+
)
43+
from Z import A
44+
45+
import flake8_import_order
46+
47+
import tests # I202
48+
49+
from . import A
50+
51+
from . import B # I202

tests/test_cases/complete_appnexus.py

+35
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,38 @@
3333
from .. import B
3434
from ..A import A
3535
from ..B import B
36+
37+
if TYPE_CHECKING:
38+
import ast
39+
from functools import *
40+
import os
41+
from os import path
42+
import StringIO
43+
import sys
44+
45+
import X
46+
from X import *
47+
from X import A
48+
from X import B, b, C, d
49+
import Y
50+
from Y import *
51+
from Y import A
52+
from Y import B, C, D
53+
from Y import e
54+
import Z
55+
from Z import A
56+
from Z.A import A
57+
from Z.A.B import A
58+
59+
from localpackage import A, b
60+
61+
import flake8_import_order
62+
from flake8_import_order import *
63+
from . import A
64+
from . import B
65+
from .A import A
66+
from .B import B
67+
from .. import A
68+
from .. import B
69+
from ..A import A
70+
from ..B import B

tests/test_cases/complete_cryptography.py

+42
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,45 @@
4040
from .. import B
4141
from ..A import A
4242
from ..B import B
43+
44+
if TYPE_CHECKING:
45+
import ast
46+
import os
47+
import sys
48+
from functools import *
49+
from os import path
50+
51+
import X
52+
from X import *
53+
from X import A
54+
from X import B, C, D
55+
56+
import Y
57+
from Y import *
58+
from Y import A
59+
from Y import B, C, D
60+
61+
import Z
62+
from Z import A
63+
from Z.A import A
64+
from Z.A.B import A
65+
66+
import localpackage
67+
68+
import flake8_import_order
69+
from flake8_import_order import *
70+
from flake8_import_order import A
71+
from flake8_import_order import B
72+
73+
import tests
74+
from tests import A
75+
from tests import B
76+
77+
from . import A
78+
from . import B
79+
from .A import A
80+
from .B import B
81+
from .. import A
82+
from .. import B
83+
from ..A import A
84+
from ..B import B

tests/test_cases/complete_edited.py

+38
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,41 @@
3636
from .. import B
3737
from ..A import A
3838
from ..B import B
39+
40+
if TYPE_CHECKING:
41+
import ast
42+
import os
43+
import StringIO
44+
import sys
45+
from functools import *
46+
from os import path
47+
48+
import X
49+
import Y
50+
import Z
51+
from X import *
52+
from X import A
53+
from X import B, b, C, d
54+
from Y import *
55+
from Y import A
56+
from Y import B, C, D
57+
from Y import e
58+
from Z import A
59+
from Z.A import A
60+
from Z.A.B import A
61+
62+
import localpackage
63+
64+
from localpackage import A, b
65+
66+
import flake8_import_order
67+
from flake8_import_order import *
68+
69+
from . import A
70+
from . import B
71+
from .A import A
72+
from .B import B
73+
from .. import A
74+
from .. import B
75+
from ..A import A
76+
from ..B import B

tests/test_cases/complete_google.py

+34
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,37 @@
3232
from .. import B
3333
from ..A import A
3434
from ..B import B
35+
36+
if TYPE_CHECKING:
37+
import ast
38+
from functools import *
39+
import os
40+
from os import path
41+
import StringIO
42+
import sys
43+
44+
import localpackage
45+
import X
46+
from X import *
47+
from X import A
48+
from X import B, b, C, d
49+
import Y
50+
from Y import *
51+
from Y import A
52+
from Y import B, C, D
53+
from Y import e
54+
import Z
55+
from Z import A
56+
from Z.A import A
57+
from Z.A.B import A
58+
59+
import flake8_import_order
60+
from flake8_import_order import *
61+
from . import A
62+
from . import B
63+
from .A import A
64+
from .B import B
65+
from .. import A
66+
from .. import B
67+
from ..A import A
68+
from ..B import B

tests/test_cases/complete_pep8.py

+31
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,34 @@
2929
from .. import A
3030
from ..B import B
3131
from ..A import A
32+
33+
if TYPE_CHECKING:
34+
import sys
35+
from os import path
36+
import os
37+
from functools import *
38+
import ast
39+
40+
import localpackage
41+
import X
42+
from X import A, d
43+
from X import *
44+
import Z
45+
from Z import A
46+
from Z.A.B import A
47+
from Z.A import A
48+
import Y
49+
from Y import *
50+
from Y import B
51+
from Y import A, C, D
52+
53+
import flake8_import_order
54+
from flake8_import_order import *
55+
from . import B
56+
from . import A
57+
from .B import B
58+
from .A import A
59+
from .. import B
60+
from .. import A
61+
from ..B import B
62+
from ..A import A

0 commit comments

Comments
 (0)