@@ -839,6 +839,53 @@ def imports_on_separate_lines(logical_line):
839
839
yield found , "E401 multiple imports on one line"
840
840
841
841
842
+ def module_imports_on_top_of_file (
843
+ logical_line , indent_level , checker_state , noqa ):
844
+ r"""Imports are always put at the top of the file, just after any module
845
+ comments and docstrings, and before module globals and constants.
846
+
847
+ Okay: import os
848
+ Okay: # this is a comment\nimport os
849
+ Okay: '''this is a module docstring'''\nimport os
850
+ Okay: r'''this is a module docstring'''\nimport os
851
+ Okay: __version__ = "123"\nimport os
852
+ E402: a=1\nimport os
853
+ E402: 'One string'\n"Two string"\nimport os
854
+ E402: a=1\nfrom sys import x
855
+
856
+ Okay: if x:\n import os
857
+ """
858
+ def is_string_literal (line ):
859
+ if line [0 ] in 'uUbB' :
860
+ line = line [1 :]
861
+ if line and line [0 ] in 'rR' :
862
+ line = line [1 :]
863
+ return line and (line [0 ] == '"' or line [0 ] == "'" )
864
+
865
+ if indent_level : # Allow imports in conditional statements or functions
866
+ return
867
+ if not logical_line : # Allow empty lines or comments
868
+ return
869
+ if noqa :
870
+ return
871
+ line = logical_line
872
+ if line .startswith ('import ' ) or line .startswith ('from ' ):
873
+ if checker_state .get ('seen_non_imports' , False ):
874
+ yield 0 , "E402 import not at top of file"
875
+ elif line .startswith ('__version__ ' ):
876
+ # These lines should be included after the module's docstring, before
877
+ # any other code, separated by a blank line above and below.
878
+ return
879
+ elif is_string_literal (line ):
880
+ # The first literal is a docstring, allow it. Otherwise, report error.
881
+ if checker_state .get ('seen_docstring' , False ):
882
+ checker_state ['seen_non_imports' ] = True
883
+ else :
884
+ checker_state ['seen_docstring' ] = True
885
+ else :
886
+ checker_state ['seen_non_imports' ] = True
887
+
888
+
842
889
def compound_statements (logical_line ):
843
890
r"""Compound statements (on the same line) are generally discouraged.
844
891
@@ -1251,6 +1298,8 @@ def __init__(self, filename=None, lines=None,
1251
1298
self .hang_closing = options .hang_closing
1252
1299
self .verbose = options .verbose
1253
1300
self .filename = filename
1301
+ # Dictionary where a checker can store its custom state.
1302
+ self ._checker_states = {}
1254
1303
if filename is None :
1255
1304
self .filename = 'stdin'
1256
1305
self .lines = lines or []
@@ -1306,10 +1355,16 @@ def run_check(self, check, argument_names):
1306
1355
arguments .append (getattr (self , name ))
1307
1356
return check (* arguments )
1308
1357
1358
+ def init_checker_state (self , name , argument_names ):
1359
+ """ Prepares a custom state for the specific checker plugin."""
1360
+ if 'checker_state' in argument_names :
1361
+ self .checker_state = self ._checker_states .setdefault (name , {})
1362
+
1309
1363
def check_physical (self , line ):
1310
1364
"""Run all physical checks on a raw input line."""
1311
1365
self .physical_line = line
1312
1366
for name , check , argument_names in self ._physical_checks :
1367
+ self .init_checker_state (name , argument_names )
1313
1368
result = self .run_check (check , argument_names )
1314
1369
if result is not None :
1315
1370
(offset , text ) = result
@@ -1368,6 +1423,7 @@ def check_logical(self):
1368
1423
for name , check , argument_names in self ._logical_checks :
1369
1424
if self .verbose >= 4 :
1370
1425
print (' ' + name )
1426
+ self .init_checker_state (name , argument_names )
1371
1427
for offset , text in self .run_check (check , argument_names ) or ():
1372
1428
if not isinstance (offset , tuple ):
1373
1429
for token_offset , pos in mapping :
0 commit comments