54
54
import inspect
55
55
import keyword
56
56
import tokenize
57
+ import ast
57
58
from optparse import OptionParser
58
59
from fnmatch import fnmatch
59
60
try :
@@ -752,16 +753,13 @@ def whitespace_around_named_parameter_equals(logical_line, tokens):
752
753
Don't use spaces around the '=' sign when used to indicate a
753
754
keyword argument or a default parameter value.
754
755
755
- Okay: def complex(real, imag=0.0):
756
- Okay: return magic(r=real, i=imag)
756
+ Okay: def complex(real, imag=0.0):\n return magic(r=real, i=imag)
757
757
Okay: boolean(a == b)
758
758
Okay: boolean(a != b)
759
759
Okay: boolean(a <= b)
760
760
Okay: boolean(a >= b)
761
- Okay: def foo(arg: int = 42):
762
-
763
- E251: def complex(real, imag = 0.0):
764
- E251: return magic(r = real, i = imag)
761
+ Okay: def foo(arg: int = 42):\n pass
762
+ E251: def complex(real, imag = 0.0):\n return magic(r = real, i = imag)
765
763
"""
766
764
parens = 0
767
765
no_space = False
@@ -781,9 +779,9 @@ def whitespace_around_named_parameter_equals(logical_line, tokens):
781
779
parens += 1
782
780
elif text == ')' :
783
781
parens -= 1
784
- elif in_def and text == ':' and parens == 1 :
782
+ elif parens == 1 and in_def and text == ':' :
785
783
annotated_func_arg = True
786
- elif parens and text == ',' and parens == 1 :
784
+ elif parens == 1 and text == ',' :
787
785
annotated_func_arg = False
788
786
elif parens and text == '=' and not annotated_func_arg :
789
787
no_space = True
@@ -1013,7 +1011,7 @@ def break_around_binary_operator(logical_line, tokens):
1013
1011
1014
1012
Okay: (width == 0 +\n height == 0)
1015
1013
Okay: foo(\n -x)
1016
- Okay: foo(x\n [])
1014
+ Okay: foo(x, \n [])
1017
1015
Okay: x = '''\n''' + ''
1018
1016
Okay: foo(x,\n -y)
1019
1017
Okay: foo(x, # comment\n -y)
@@ -1046,11 +1044,11 @@ def comparison_to_singleton(logical_line, noqa):
1046
1044
Comparisons to singletons like None should always be done
1047
1045
with "is" or "is not", never the equality operators.
1048
1046
1049
- Okay: if arg is not None:
1050
- E711: if arg != None:
1051
- E711: if None == arg:
1052
- E712: if arg == True:
1053
- E712: if False == arg:
1047
+ Okay: arg is not None
1048
+ E711: arg != None
1049
+ E711: None == arg
1050
+ E712: arg == True
1051
+ E712: False == arg
1054
1052
1055
1053
Also, beware of writing if x when you really mean if x is not None --
1056
1054
e.g. when testing whether a variable or argument that defaults to None was
@@ -1100,15 +1098,15 @@ def comparison_type(logical_line, noqa):
1100
1098
1101
1099
Do not compare types directly.
1102
1100
1103
- Okay: if isinstance(obj, int):
1104
- E721: if type(obj) is type(1):
1101
+ Okay: isinstance(obj, int)
1102
+ E721: type(obj) is type(1)
1105
1103
1106
1104
When checking if an object is a string, keep in mind that it might be a
1107
1105
unicode string too! In Python 2.3, str and unicode have a common base
1108
1106
class, basestring, so you can do:
1109
1107
1110
- Okay: if isinstance(obj, basestring):
1111
- Okay: if type(a1) is type(b1):
1108
+ Okay: isinstance(obj, basestring)
1109
+ Okay: type(a1) is type(b1)
1112
1110
"""
1113
1111
match = COMPARE_TYPE_REGEX .search (logical_line )
1114
1112
if match and not noqa :
@@ -1121,7 +1119,7 @@ def comparison_type(logical_line, noqa):
1121
1119
def python_3000_has_key (logical_line , noqa ):
1122
1120
r"""The {}.has_key() method is removed in Python 3: use the 'in' operator.
1123
1121
1124
- Okay: if "alph" in d:\n print d["alph"]
1122
+ Okay: "alph" in d
1125
1123
W601: assert d.has_key('alph')
1126
1124
"""
1127
1125
pos = logical_line .find ('.has_key(' )
@@ -1147,8 +1145,8 @@ def python_3000_not_equal(logical_line):
1147
1145
1148
1146
The older syntax is removed in Python 3.
1149
1147
1150
- Okay: if a != 'no':
1151
- W603: if a <> 'no':
1148
+ Okay: a != 'no'
1149
+ W603: a <> 'no'
1152
1150
"""
1153
1151
pos = logical_line .find ('<>' )
1154
1152
if pos > - 1 :
@@ -1166,6 +1164,105 @@ def python_3000_backticks(logical_line):
1166
1164
yield pos , "W604 backticks are deprecated, use 'repr()'"
1167
1165
1168
1166
1167
+ def inexisting_last_line_is_reachable (tree ):
1168
+ r"""Return true if inexisting last line of ast tree is reachable.
1169
+
1170
+ Detecting whether the last line of some piece of code is reachable or not
1171
+ corresponds to solving the halting problem which is known to be impossible.
1172
+ However, we could solve a relaxed version of this : indeed, we may assume
1173
+ that:
1174
+ - all code is written for a reason and is supposed to be reachable.
1175
+ - only a limited amount of statements may break the reachable property:
1176
+ * return statements
1177
+ * raise statements
1178
+ * assert False.
1179
+ We'll consider the last line to be reachable if TODO
1180
+ """
1181
+ assert isinstance (tree , ast .AST )
1182
+ children = list (ast .iter_child_nodes (tree ))
1183
+ last_child = children [- 1 ]
1184
+ # These stop reachability
1185
+ if isinstance (last_child , ast .Return ):
1186
+ return False
1187
+ elif isinstance (last_child , ast .Raise ):
1188
+ return False
1189
+ elif isinstance (last_child , ast .Assert ):
1190
+ test_val = last_child .test
1191
+ if isinstance (test_val , ast .Name ) and test_val .id == "False" :
1192
+ return False
1193
+ if isinstance (test_val , ast .NameConstant ) and not test_val .value :
1194
+ return False
1195
+ # These propagage reachability
1196
+ elif isinstance (last_child , ast .If ):
1197
+ then_body , else_body = last_child .body , last_child .orelse
1198
+ if not else_body :
1199
+ return True
1200
+ return any (inexisting_last_line_is_reachable (branch [- 1 ])
1201
+ for branch in (then_body , else_body ))
1202
+ # elif isinstance(last_child, ast.For):
1203
+ # pass # print(last_child)
1204
+ # elif isinstance(last_child, ast.While):
1205
+ # pass # print(last_child)
1206
+ # Otherwise, it is reachable
1207
+ print (last_child )
1208
+ return False # will be true at the end
1209
+
1210
+
1211
+ class UnconsistentReturns ():
1212
+ r"""This doc mentions W700 and W701."""
1213
+
1214
+ def __init__ (self , tree , filename ):
1215
+ r"""This doc mentions W700 and W701."""
1216
+ # print("init1")
1217
+ self .tree = tree
1218
+ self .filename = filename
1219
+
1220
+ def run (self ):
1221
+ r"""Run the check."""
1222
+ return UnconsistentReturns .check_in_tree (self .tree )
1223
+
1224
+ @staticmethod
1225
+ def check_in_tree (tree ):
1226
+ r"""Check for inconsistent returns in tree."""
1227
+ assert isinstance (tree , ast .AST )
1228
+ for node in ast .walk (tree ):
1229
+ if isinstance (node , ast .FunctionDef ):
1230
+ for err in UnconsistentReturns .check_in_func (node ):
1231
+ yield err
1232
+
1233
+ @staticmethod
1234
+ def check_in_func (func_node ):
1235
+ r"""Check for inconsistent returns (with or without values) in function.
1236
+
1237
+ Functions should either return an explicit value in all return
1238
+ statements (including the final value-less implicit return if
1239
+ reachable) or in none of them.
1240
+ """
1241
+ assert isinstance (func_node , ast .FunctionDef )
1242
+ returns = UnconsistentReturns .collect_return_nodes (func_node )
1243
+ returns_value = [ret for ret in returns if ret .value is not None ]
1244
+ if returns_value :
1245
+ for r in returns :
1246
+ if r .value is None :
1247
+ yield (r .lineno , r .col_offset ,
1248
+ "W700 unconsistent return values" , "toto" )
1249
+ if inexisting_last_line_is_reachable (func_node ):
1250
+ yield (func_node .lineno , func_node .col_offset ,
1251
+ "W701 unconsistent return values" , "toto" )
1252
+
1253
+ @staticmethod
1254
+ def collect_return_nodes (func_node ):
1255
+ r"""Collect return nodes from the node describing a function.
1256
+
1257
+ The tricky part is not to get the nodes corresponding to return
1258
+ statements in nested function definitions.
1259
+ """
1260
+ # THIS DOES NOT HANDLE NESTED FUNCTIONS PROPERLY
1261
+ assert isinstance (func_node , ast .FunctionDef )
1262
+ return [node for node in ast .walk (func_node )
1263
+ if isinstance (node , ast .Return )]
1264
+
1265
+
1169
1266
##############################################################################
1170
1267
# Helper functions
1171
1268
##############################################################################
@@ -1321,32 +1418,40 @@ def _get_parameters(function):
1321
1418
return inspect .getargspec (function )[0 ]
1322
1419
1323
1420
1324
- def register_check (check , codes = None ):
1421
+ def register_check (check ):
1325
1422
"""Register a new check object."""
1326
- def _add_check (check , kind , codes , args ):
1423
+ def _add_check (check , kind , args ):
1424
+ # print(check, kind, len(_checks[kind]))
1425
+ codes = ERRORCODE_REGEX .findall (check .__doc__ or '' )
1327
1426
if check in _checks [kind ]:
1328
1427
_checks [kind ][check ][0 ].extend (codes or [])
1329
1428
else :
1330
1429
_checks [kind ][check ] = (codes or ['' ], args )
1430
+ # print("_add_check", kind, check.__name__, len(_checks[kind]))
1331
1431
if inspect .isfunction (check ):
1332
1432
args = _get_parameters (check )
1333
1433
if args and args [0 ] in ('physical_line' , 'logical_line' ):
1334
- if codes is None :
1335
- codes = ERRORCODE_REGEX .findall (check .__doc__ or '' )
1336
- _add_check (check , args [0 ], codes , args )
1434
+ _add_check (check , args [0 ], args )
1337
1435
elif inspect .isclass (check ):
1338
- if _get_parameters (check .__init__ )[:2 ] == ['self' , 'tree' ]:
1339
- _add_check (check , 'tree' , codes , None )
1436
+ if check .__module__ in ("pep8" , "__main__" ): # HACK
1437
+ args = _get_parameters (check .__init__ )
1438
+ if args and args [0 ] == 'self' and args [1 ] == 'tree' :
1439
+ # print(check, args)
1440
+ _add_check (check , args [1 ], args )
1340
1441
1341
1442
1342
1443
def init_checks_registry ():
1343
1444
"""Register all globally visible functions.
1344
1445
1345
1446
The first argument name is either 'physical_line' or 'logical_line'.
1346
1447
"""
1448
+ print ("init_checks_registry begin" )
1347
1449
mod = inspect .getmodule (register_check )
1348
1450
for (name , function ) in inspect .getmembers (mod , inspect .isfunction ):
1349
1451
register_check (function )
1452
+ for (name , klass ) in inspect .getmembers (mod , inspect .isclass ):
1453
+ register_check (klass )
1454
+ print ("init_checks_registry end" )
1350
1455
init_checks_registry ()
1351
1456
1352
1457
@@ -1363,6 +1468,7 @@ def __init__(self, filename=None, lines=None,
1363
1468
self ._physical_checks = options .physical_checks
1364
1469
self ._logical_checks = options .logical_checks
1365
1470
self ._ast_checks = options .ast_checks
1471
+ # print("options.ast_checks", options.ast_checks)
1366
1472
self .max_line_length = options .max_line_length
1367
1473
self .multiline = False # in a multiline string?
1368
1474
self .hang_closing = options .hang_closing
@@ -1420,21 +1526,16 @@ def readline(self):
1420
1526
1421
1527
def run_check (self , check , argument_names ):
1422
1528
"""Run a check plugin."""
1423
- arguments = []
1424
- for name in argument_names :
1425
- arguments .append (getattr (self , name ))
1426
- return check (* arguments )
1427
-
1428
- def init_checker_state (self , name , argument_names ):
1429
- """ Prepares a custom state for the specific checker plugin."""
1430
1529
if 'checker_state' in argument_names :
1431
- self .checker_state = self ._checker_states .setdefault (name , {})
1530
+ self .checker_state = self ._checker_states .setdefault (
1531
+ check .__name__ , {})
1532
+ arguments = [getattr (self , name ) for name in argument_names ]
1533
+ return check (* arguments )
1432
1534
1433
1535
def check_physical (self , line ):
1434
1536
"""Run all physical checks on a raw input line."""
1435
1537
self .physical_line = line
1436
- for name , check , argument_names in self ._physical_checks :
1437
- self .init_checker_state (name , argument_names )
1538
+ for check , argument_names in self ._physical_checks :
1438
1539
result = self .run_check (check , argument_names )
1439
1540
if result is not None :
1440
1541
(offset , text ) = result
@@ -1490,10 +1591,7 @@ def check_logical(self):
1490
1591
self .blank_before = self .blank_lines
1491
1592
if self .verbose >= 2 :
1492
1593
print (self .logical_line [:80 ].rstrip ())
1493
- for name , check , argument_names in self ._logical_checks :
1494
- if self .verbose >= 4 :
1495
- print (' ' + name )
1496
- self .init_checker_state (name , argument_names )
1594
+ for check , argument_names in self ._logical_checks :
1497
1595
for offset , text in self .run_check (check , argument_names ) or ():
1498
1596
if not isinstance (offset , tuple ):
1499
1597
for token_offset , pos in mapping :
@@ -1509,11 +1607,13 @@ def check_logical(self):
1509
1607
1510
1608
def check_ast (self ):
1511
1609
"""Build the file's AST and run all AST checks."""
1610
+ # print("check_ast")
1512
1611
try :
1513
1612
tree = compile ('' .join (self .lines ), '' , 'exec' , PyCF_ONLY_AST )
1514
1613
except (ValueError , SyntaxError , TypeError ):
1515
1614
return self .report_invalid_syntax ()
1516
- for name , cls , __ in self ._ast_checks :
1615
+ for cls , __ in self ._ast_checks :
1616
+ # print("toto", cls)
1517
1617
checker = cls (tree , self .filename )
1518
1618
for lineno , offset , text , check in checker .run ():
1519
1619
if not self .lines or not noqa (self .lines [lineno - 1 ]):
@@ -1565,9 +1665,11 @@ def maybe_check_physical(self, token):
1565
1665
1566
1666
def check_all (self , expected = None , line_offset = 0 ):
1567
1667
"""Run all checks on the input file."""
1668
+ # print("check_all")
1568
1669
self .report .init_file (self .filename , self .lines , expected , line_offset )
1569
1670
self .total_lines = len (self .lines )
1570
1671
if self ._ast_checks :
1672
+ # print("toto3")
1571
1673
self .check_ast ()
1572
1674
self .line_number = 0
1573
1675
self .indent_char = None
@@ -1817,6 +1919,9 @@ def __init__(self, *args, **kwargs):
1817
1919
options .physical_checks = self .get_checks ('physical_line' )
1818
1920
options .logical_checks = self .get_checks ('logical_line' )
1819
1921
options .ast_checks = self .get_checks ('tree' )
1922
+ # print("options.physical_checks", len(options.physical_checks))
1923
+ # print("options.logical_checks", len(options.logical_checks))
1924
+ # print("options.ast_checks", len(options.ast_checks))
1820
1925
self .init_report ()
1821
1926
1822
1927
def init_report (self , reporter = None ):
@@ -1894,24 +1999,23 @@ def ignore_code(self, code):
1894
1999
return False. Else, if 'options.ignore' contains a prefix of
1895
2000
the error code, return True.
1896
2001
"""
1897
- if len ( code ) < 4 and any ( s . startswith ( code )
1898
- for s in self . options .select ):
2002
+ options = self . options
2003
+ if len ( code ) < 4 and any ( s . startswith ( code ) for s in options .select ):
1899
2004
return False
1900
- return (code .startswith (self . options .ignore ) and
1901
- not code .startswith (self . options .select ))
2005
+ return (code .startswith (options .ignore ) and
2006
+ not code .startswith (options .select ))
1902
2007
1903
2008
def get_checks (self , argument_name ):
1904
2009
"""Get all the checks for this category.
1905
2010
1906
2011
Find all globally visible functions where the first argument name
1907
2012
starts with argument_name and which contain selected tests.
1908
2013
"""
1909
- checks = []
1910
- for check , attrs in _checks [argument_name ].items ():
1911
- (codes , args ) = attrs
1912
- if any (not (code and self .ignore_code (code )) for code in codes ):
1913
- checks .append ((check .__name__ , check , args ))
1914
- return sorted (checks )
2014
+ checks = [(check , args )
2015
+ for check , (codes , args ) in _checks [argument_name ].items ()
2016
+ if any (not self .ignore_code (code ) for code in codes )]
2017
+ print (argument_name , len (checks ), len (_checks [argument_name ]))
2018
+ return sorted (checks , key = lambda arg : arg [0 ].__name__ )
1915
2019
1916
2020
1917
2021
def get_parser (prog = 'pep8' , version = __version__ ):
0 commit comments