@@ -835,6 +835,52 @@ def imports_on_separate_lines(logical_line):
835
835
yield found , "E401 multiple imports on one line"
836
836
837
837
838
+ def imports_on_top_of_file (logical_line , indent_level , checker_state , noqa ):
839
+ r"""Imports are always put at the top of the file, just after any module
840
+ comments and docstrings, and before module globals and constants.
841
+
842
+ Okay: import os
843
+ Okay: # this is a comment\nimport os
844
+ Okay: '''this is a module docstring'''\nimport os
845
+ Okay: r'''this is a module docstring'''\nimport os
846
+ Okay: __version__ = "123"\nimport os
847
+ E402: a=1\nimport os
848
+ E402: 'One string'\n"Two string"\nimport os
849
+ E402: a=1\nfrom sys import x
850
+
851
+ Okay: if x:\n import os
852
+ """
853
+ def is_string_literal (line ):
854
+ if line [0 ] in 'uUbB' :
855
+ line = line [1 :]
856
+ if line and line [0 ] in 'rR' :
857
+ line = line [1 :]
858
+ return line and (line [0 ] == '"' or line [0 ] == "'" )
859
+
860
+ if indent_level : # Allow imports in conditional statements or functions
861
+ return
862
+ if not logical_line : # Allow empty lines or comments
863
+ return
864
+ if noqa :
865
+ return
866
+ line = logical_line
867
+ if line .startswith ('import ' ) or line .startswith ('from ' ):
868
+ if checker_state .get ('seen_non_imports' , False ):
869
+ yield 0 , "E402 import not at top of file"
870
+ elif line .startswith ('__version__ ' ):
871
+ # These lines should be included after the module's docstring, before
872
+ # any other code, separated by a blank line above and below.
873
+ return
874
+ elif is_string_literal (line ):
875
+ # The first literal is a docstring, allow it. Otherwise, report error.
876
+ if checker_state .get ('seen_docstring' , False ):
877
+ checker_state ['seen_non_imports' ] = True
878
+ else :
879
+ checker_state ['seen_docstring' ] = True
880
+ else :
881
+ checker_state ['seen_non_imports' ] = True
882
+
883
+
838
884
def compound_statements (logical_line ):
839
885
r"""Compound statements (on the same line) are generally discouraged.
840
886
@@ -1239,6 +1285,8 @@ def __init__(self, filename=None, lines=None,
1239
1285
self .hang_closing = options .hang_closing
1240
1286
self .verbose = options .verbose
1241
1287
self .filename = filename
1288
+ # Dictionary where a checker can store its custom state.
1289
+ self ._checker_states = {}
1242
1290
if filename is None :
1243
1291
self .filename = 'stdin'
1244
1292
self .lines = lines or []
@@ -1294,10 +1342,16 @@ def run_check(self, check, argument_names):
1294
1342
arguments .append (getattr (self , name ))
1295
1343
return check (* arguments )
1296
1344
1345
+ def init_checker_state (self , name , argument_names ):
1346
+ """ Prepares a custom state for the specific checker plugin."""
1347
+ if 'checker_state' in argument_names :
1348
+ self .checker_state = self ._checker_states .setdefault (name , {})
1349
+
1297
1350
def check_physical (self , line ):
1298
1351
"""Run all physical checks on a raw input line."""
1299
1352
self .physical_line = line
1300
1353
for name , check , argument_names in self ._physical_checks :
1354
+ self .init_checker_state (name , argument_names )
1301
1355
result = self .run_check (check , argument_names )
1302
1356
if result is not None :
1303
1357
(offset , text ) = result
@@ -1352,6 +1406,7 @@ def check_logical(self):
1352
1406
for name , check , argument_names in self ._logical_checks :
1353
1407
if self .verbose >= 4 :
1354
1408
print (' ' + name )
1409
+ self .init_checker_state (name , argument_names )
1355
1410
for offset , text in self .run_check (check , argument_names ) or ():
1356
1411
if not isinstance (offset , tuple ):
1357
1412
for token_offset , pos in mapping :
0 commit comments