655 E701: try: something() |
844 E701: try: something() |
656 E701: finally: cleanup() |
845 E701: finally: cleanup() |
657 E701: if foo == 'blah': one(); two(); three() |
846 E701: if foo == 'blah': one(); two(); three() |
658 |
847 |
659 E702: do_one(); do_two(); do_three() |
848 E702: do_one(); do_two(); do_three() |
|
849 E703: do_four(); # useless semicolon |
660 """ |
850 """ |
661 line = logical_line |
851 line = logical_line |
|
852 last_char = len(line) - 1 |
662 found = line.find(':') |
853 found = line.find(':') |
663 if -1 < found < len(line) - 1: |
854 while -1 < found < last_char: |
664 before = line[:found] |
855 before = line[:found] |
665 if (before.count('{') <= before.count('}') and # {'a': 1} (dict) |
856 if (before.count('{') <= before.count('}') and # {'a': 1} (dict) |
666 before.count('[') <= before.count(']') and # [1:2] (slice) |
857 before.count('[') <= before.count(']') and # [1:2] (slice) |
667 not re.search(r'\blambda\b', before)): # lambda x: x |
858 before.count('(') <= before.count(')') and # (Python 3 annotation) |
668 return found, "E701" |
859 not LAMBDA_REGEX.search(before)): # lambda x: x |
|
860 yield found, "E701" |
|
861 found = line.find(':', found + 1) |
669 found = line.find(';') |
862 found = line.find(';') |
670 if -1 < found: |
863 while -1 < found: |
671 return found, "E702" |
864 if found < last_char: |
672 |
865 yield found, "E702" |
673 |
866 else: |
674 def python_3000_has_key(logical_line): |
867 yield found, "E703" |
675 """ |
868 found = line.find(';', found + 1) |
676 The {}.has_key() method will be removed in the future version of |
869 |
677 Python. Use the 'in' operation instead, like: |
870 |
678 d = {"a": 1, "b": 2} |
871 def explicit_line_join(logical_line, tokens): |
679 if "b" in d: |
872 r""" |
680 print d["b"] |
873 Avoid explicit line join between brackets. |
|
874 |
|
875 The preferred way of wrapping long lines is by using Python's implied line |
|
876 continuation inside parentheses, brackets and braces. Long lines can be |
|
877 broken over multiple lines by wrapping expressions in parentheses. These |
|
878 should be used in preference to using a backslash for line continuation. |
|
879 |
|
880 E502: aaa = [123, \\n 123] |
|
881 E502: aaa = ("bbb " \\n "ccc") |
|
882 |
|
883 Okay: aaa = [123,\n 123] |
|
884 Okay: aaa = ("bbb "\n "ccc") |
|
885 Okay: aaa = "bbb " \\n "ccc" |
|
886 """ |
|
887 prev_start = prev_end = parens = 0 |
|
888 backslash = None |
|
889 for token_type, text, start, end, line in tokens: |
|
890 if start[0] != prev_start and parens and backslash: |
|
891 yield backslash, "E502" |
|
892 if end[0] != prev_end: |
|
893 if line.rstrip('\r\n').endswith('\\'): |
|
894 backslash = (end[0], len(line.splitlines()[-1]) - 1) |
|
895 else: |
|
896 backslash = None |
|
897 prev_start = prev_end = end[0] |
|
898 else: |
|
899 prev_start = start[0] |
|
900 if token_type == tokenize.OP: |
|
901 if text in '([{': |
|
902 parens += 1 |
|
903 elif text in ')]}': |
|
904 parens -= 1 |
|
905 |
|
906 |
|
907 def comparison_to_singleton(logical_line, noqa): |
|
908 """ |
|
909 Comparisons to singletons like None should always be done |
|
910 with "is" or "is not", never the equality operators. |
|
911 |
|
912 Okay: if arg is not None: |
|
913 E711: if arg != None: |
|
914 E712: if arg == True: |
|
915 |
|
916 Also, beware of writing if x when you really mean if x is not None -- |
|
917 e.g. when testing whether a variable or argument that defaults to None was |
|
918 set to some other value. The other value might have a type (such as a |
|
919 container) that could be false in a boolean context! |
|
920 """ |
|
921 match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line) |
|
922 if match: |
|
923 same = (match.group(1) == '==') |
|
924 singleton = match.group(2) |
|
925 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton) |
|
926 if singleton in ('None',): |
|
927 code = 'E711' |
|
928 else: |
|
929 code = 'E712' |
|
930 nonzero = ((singleton == 'True' and same) or |
|
931 (singleton == 'False' and not same)) |
|
932 msg += " or 'if %scond:'" % ('' if nonzero else 'not ') |
|
933 yield match.start(1), code, singleton, msg |
|
934 |
|
935 |
|
936 def comparison_type(logical_line): |
|
937 """ |
|
938 Object type comparisons should always use isinstance() instead of |
|
939 comparing types directly. |
|
940 |
|
941 Okay: if isinstance(obj, int): |
|
942 E721: if type(obj) is type(1): |
|
943 |
|
944 When checking if an object is a string, keep in mind that it might be a |
|
945 unicode string too! In Python 2.3, str and unicode have a common base |
|
946 class, basestring, so you can do: |
|
947 |
|
948 Okay: if isinstance(obj, basestring): |
|
949 Okay: if type(a1) is type(b1): |
|
950 """ |
|
951 match = COMPARE_TYPE_REGEX.search(logical_line) |
|
952 if match: |
|
953 inst = match.group(1) |
|
954 if inst and isidentifier(inst) and inst not in SINGLETONS: |
|
955 return # Allow comparison for types which are not obvious |
|
956 yield match.start(), "E721" |
|
957 |
|
958 |
|
959 r""" |
|
960 The {}.has_key() method is removed in the Python 3. |
|
961 Use the 'in' operation instead. |
|
962 |
|
963 Okay: if "alph" in d:\n print d["alph"] |
|
964 W601: assert d.has_key('alph') |
681 """ |
965 """ |
682 pos = logical_line.find('.has_key(') |
966 pos = logical_line.find('.has_key(') |
683 if pos > -1: |
967 if pos > -1: |
684 return pos, "W601" |
968 yield pos, "W601" |
685 |
969 |
686 |
970 |
687 def python_3000_raise_comma(logical_line): |
971 def python_3000_raise_comma(logical_line): |
688 """ |
972 """ |
689 When raising an exception, use "raise ValueError('message')" |
973 When raising an exception, use "raise ValueError('message')" |
690 instead of the older form "raise ValueError, 'message'". |
974 instead of the older form "raise ValueError, 'message'". |
691 |
975 |
692 The paren-using form is preferred because when the exception arguments |
976 The paren-using form is preferred because when the exception arguments |
693 are long or include string formatting, you don't need to use line |
977 are long or include string formatting, you don't need to use line |
694 continuation characters thanks to the containing parentheses. The older |
978 continuation characters thanks to the containing parentheses. The older |
695 form will be removed in Python 3000. |
979 form is removed in Python 3. |
|
980 |
|
981 Okay: raise DummyError("Message") |
|
982 W602: raise DummyError, "Message" |
696 """ |
983 """ |
697 match = RAISE_COMMA_REGEX.match(logical_line) |
984 match = RAISE_COMMA_REGEX.match(logical_line) |
698 if match: |
985 if match and not RERAISE_COMMA_REGEX.match(logical_line): |
699 return match.start(1), "W602" |
986 yield match.end() - 1, "W602" |
700 |
987 |
701 |
988 |
702 def python_3000_not_equal(logical_line): |
989 def python_3000_not_equal(logical_line): |
703 """ |
990 """ |
704 != can also be written <>, but this is an obsolete usage kept for |
991 != can also be written <>, but this is an obsolete usage kept for |
705 backwards compatibility only. New code should always use !=. |
992 backwards compatibility only. New code should always use !=. |
706 The older syntax is removed in Python 3000. |
993 The older syntax is removed in Python 3. |
|
994 |
|
995 Okay: if a != 'no': |
|
996 W603: if a <> 'no': |
707 """ |
997 """ |
708 pos = logical_line.find('<>') |
998 pos = logical_line.find('<>') |
709 if pos > -1: |
999 if pos > -1: |
710 return pos, "W603" |
1000 yield pos, "W603" |
711 |
1001 |
712 |
1002 |
713 def python_3000_backticks(logical_line): |
1003 def python_3000_backticks(logical_line): |
714 """ |
1004 """ |
715 Backticks are removed in Python 3000. |
1005 Backticks are removed in Python 3. |
716 Use repr() instead. |
1006 Use repr() instead. |
|
1007 |
|
1008 Okay: val = repr(1 + 2) |
|
1009 W604: val = `1 + 2` |
717 """ |
1010 """ |
718 pos = logical_line.find('`') |
1011 pos = logical_line.find('`') |
719 if pos > -1: |
1012 if pos > -1: |
720 return pos, "W604" |
1013 yield pos, "W604" |
721 |
1014 |
722 |
1015 |
723 ############################################################################## |
1016 ############################################################################## |
724 # Helper functions |
1017 # Helper functions |
725 ############################################################################## |
1018 ############################################################################## |
726 |
1019 |
727 |
1020 |
728 if '' == ''.encode(): |
1021 if '' == ''.encode(): |
729 # Python 2: implicit encoding. |
1022 # Python 2: implicit encoding. |
730 def readlines(filename): |
1023 def readlines(filename): |
731 return open(filename).readlines() |
1024 f = open(filename) |
|
1025 try: |
|
1026 return f.readlines() |
|
1027 finally: |
|
1028 f.close() |
|
1029 isidentifier = re.compile(r'[a-zA-Z_]\w*').match |
|
1030 stdin_get_value = sys.stdin.read |
732 else: |
1031 else: |
733 # Python 3: decode to latin-1. |
1032 # Python 3 |
734 # This function is lazy, it does not read the encoding declaration. |
1033 def readlines(filename): # __IGNORE_WARNING__ |
735 # XXX: use tokenize.detect_encoding() |
1034 f = open(filename, 'rb') |
736 def readlines(filename): # __IGNORE_WARNING__ |
1035 try: |
737 return open(filename, encoding='latin-1').readlines() |
1036 coding, lines = tokenize.detect_encoding(f.readline) |
|
1037 f = TextIOWrapper(f, coding, line_buffering=True) |
|
1038 return [l.decode(coding) for l in lines] + f.readlines() |
|
1039 except (LookupError, SyntaxError, UnicodeError): |
|
1040 f.close() |
|
1041 # Fall back if files are improperly declared |
|
1042 f = open(filename, encoding='latin-1') |
|
1043 return f.readlines() |
|
1044 finally: |
|
1045 f.close() |
|
1046 isidentifier = str.isidentifier |
|
1047 |
|
1048 def stdin_get_value(): |
|
1049 return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() |
|
1050 readlines.__doc__ = " Read the source code." |
|
1051 noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search |
738 |
1052 |
739 |
1053 |
740 def expand_indent(line): |
1054 def expand_indent(line): |
741 """ |
1055 r""" |
742 Return the amount of indentation. |
1056 Return the amount of indentation. |
743 Tabs are expanded to the next multiple of 8. |
1057 Tabs are expanded to the next multiple of 8. |
744 |
1058 |
745 >>> expand_indent(' ') |
1059 >>> expand_indent(' ') |
746 4 |
1060 4 |
747 >>> expand_indent('\\t') |
1061 >>> expand_indent('\t') |
748 8 |
1062 8 |
749 >>> expand_indent(' \\t') |
1063 >>> expand_indent(' \t') |
750 8 |
1064 8 |
751 >>> expand_indent(' \\t') |
1065 >>> expand_indent(' \t') |
752 8 |
1066 8 |
753 >>> expand_indent(' \\t') |
1067 >>> expand_indent(' \t') |
754 16 |
1068 16 |
755 """ |
1069 """ |
|
1070 if '\t' not in line: |
|
1071 return len(line) - len(line.lstrip()) |
756 result = 0 |
1072 result = 0 |
757 for char in line: |
1073 for char in line: |
758 if char == '\t': |
1074 if char == '\t': |
759 result = result // 8 * 8 + 8 |
1075 result = result // 8 * 8 + 8 |
760 elif char == ' ': |
1076 elif char == ' ': |
884 """ |
1280 """ |
885 Build a logical line from tokens. |
1281 Build a logical line from tokens. |
886 """ |
1282 """ |
887 self.mapping = [] |
1283 self.mapping = [] |
888 logical = [] |
1284 logical = [] |
|
1285 comments = [] |
889 length = 0 |
1286 length = 0 |
890 previous = None |
1287 previous = None |
891 for token in self.tokens: |
1288 for token in self.tokens: |
892 token_type, text = token[0:2] |
1289 token_type, text = token[0:2] |
|
1290 if token_type == tokenize.COMMENT: |
|
1291 comments.append(text) |
|
1292 continue |
893 if token_type in SKIP_TOKENS: |
1293 if token_type in SKIP_TOKENS: |
894 continue |
1294 continue |
895 if token_type == tokenize.STRING: |
1295 if token_type == tokenize.STRING: |
896 text = mute_string(text) |
1296 text = mute_string(text) |
897 if previous: |
1297 if previous: |
898 end_line, end = previous[3] |
1298 end_row, end = previous[3] |
899 start_line, start = token[2] |
1299 start_row, start = token[2] |
900 if end_line != start_line: # different row |
1300 if end_row != start_row: # different row |
901 prev_text = self.lines[end_line - 1][end - 1] |
1301 prev_text = self.lines[end_row - 1][end - 1] |
902 if prev_text == ',' or (prev_text not in '{[(' |
1302 if prev_text == ',' or (prev_text not in '{[(' |
903 and text not in '}])'): |
1303 and text not in '}])'): |
904 logical.append(' ') |
1304 logical.append(' ') |
905 length += 1 |
1305 length += 1 |
906 elif end != start: # different column |
1306 elif end != start: # different column |
907 fill = self.lines[end_line - 1][end:start] |
1307 fill = self.lines[end_row - 1][end:start] |
908 logical.append(fill) |
1308 logical.append(fill) |
909 length += len(fill) |
1309 length += len(fill) |
910 self.mapping.append((length, token)) |
1310 self.mapping.append((length, token)) |
911 logical.append(text) |
1311 logical.append(text) |
912 length += len(text) |
1312 length += len(text) |
913 previous = token |
1313 previous = token |
914 self.logical_line = ''.join(logical) |
1314 self.logical_line = ''.join(logical) |
915 assert self.logical_line.lstrip() == self.logical_line |
1315 self.noqa = comments and noqa(''.join(comments)) |
916 assert self.logical_line.rstrip() == self.logical_line |
1316 # With Python 2, if the line ends with '\r\r\n' the assertion fails |
|
1317 # assert self.logical_line.strip() == self.logical_line |
917 |
1318 |
918 def check_logical(self): |
1319 def check_logical(self): |
919 """ |
1320 """ |
920 Build a line from tokens and run all logical checks on it. |
1321 Build a line from tokens and run all logical checks on it. |
921 """ |
1322 """ |
922 options.counters['logical lines'] += 1 |
|
923 self.build_tokens_line() |
1323 self.build_tokens_line() |
|
1324 self.report.increment_logical_line() |
924 first_line = self.lines[self.mapping[0][1][2][0] - 1] |
1325 first_line = self.lines[self.mapping[0][1][2][0] - 1] |
925 indent = first_line[:self.mapping[0][1][2][1]] |
1326 indent = first_line[:self.mapping[0][1][2][1]] |
926 self.previous_indent_level = self.indent_level |
1327 self.previous_indent_level = self.indent_level |
927 self.indent_level = expand_indent(indent) |
1328 self.indent_level = expand_indent(indent) |
928 if options.verbose >= 2: |
1329 if self.verbose >= 2: |
929 print(self.logical_line[:80].rstrip()) |
1330 print(self.logical_line[:80].rstrip()) |
930 for name, check, argument_names in options.logical_checks: |
1331 for name, check, argument_names in self._logical_checks: |
931 if options.verbose >= 4: |
1332 if self.verbose >= 4: |
932 print(' ' + name) |
1333 print(' ' + name) |
933 result = self.run_check(check, argument_names) |
1334 for result in self.run_check(check, argument_names): |
934 if result is not None: |
|
935 offset, code = result[:2] |
1335 offset, code = result[:2] |
936 args = result[2:] |
1336 args = result[2:] |
937 if isinstance(offset, tuple): |
1337 if isinstance(offset, tuple): |
938 original_number, original_offset = offset |
1338 orig_number, orig_offset = offset |
939 else: |
1339 else: |
940 for token_offset, token in self.mapping: |
1340 for token_offset, token in self.mapping: |
941 if offset >= token_offset: |
1341 if offset >= token_offset: |
942 original_number = token[2][0] |
1342 orig_number = token[2][0] |
943 original_offset = (token[2][1] |
1343 orig_offset = (token[2][1] + offset - token_offset) |
944 + offset - token_offset) |
1344 self.report_error_args(orig_number, orig_offset, code, check, |
945 self.report_error_args(original_number, original_offset, |
1345 *args) |
946 code, check, *args) |
|
947 self.previous_logical = self.logical_line |
1346 self.previous_logical = self.logical_line |
948 |
1347 |
|
1348 def check_ast(self): |
|
1349 try: |
|
1350 tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) |
|
1351 except (SyntaxError, TypeError): |
|
1352 return self.report_invalid_syntax() |
|
1353 for name, cls, _ in self._ast_checks: |
|
1354 checker = cls(tree, self.filename) |
|
1355 for result in checker.run(): |
|
1356 lineno, offset, code, check = result[:4] |
|
1357 args = result[4:] |
|
1358 if not noqa(self.lines[lineno - 1]): |
|
1359 self.report_error_args(lineno, offset, code, check, *args) |
|
1360 |
|
1361 def generate_tokens(self): |
|
1362 if self._io_error: |
|
1363 self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) |
|
1364 tokengen = tokenize.generate_tokens(self.readline_check_physical) |
|
1365 try: |
|
1366 for token in tokengen: |
|
1367 yield token |
|
1368 except (SyntaxError, tokenize.TokenError): |
|
1369 self.report_invalid_syntax() |
|
1370 |
949 def check_all(self, expected=None, line_offset=0): |
1371 def check_all(self, expected=None, line_offset=0): |
950 """ |
1372 """ |
951 Run all checks on the input file. |
1373 Run all checks on the input file. |
952 """ |
1374 """ |
953 self.expected = expected or () |
1375 self.report.init_file(self.filename, self.lines, expected, line_offset) |
954 self.line_offset = line_offset |
1376 if self._ast_checks: |
|
1377 self.check_ast() |
955 self.line_number = 0 |
1378 self.line_number = 0 |
956 self.file_errors = 0 |
|
957 self.indent_char = None |
1379 self.indent_char = None |
958 self.indent_level = 0 |
1380 self.indent_level = 0 |
959 self.previous_logical = '' |
1381 self.previous_logical = '' |
960 self.blank_lines = 0 |
|
961 self.blank_lines_before_comment = 0 |
|
962 self.tokens = [] |
1382 self.tokens = [] |
|
1383 self.blank_lines = blank_lines_before_comment = 0 |
963 parens = 0 |
1384 parens = 0 |
964 try: |
1385 for token in self.generate_tokens(): |
965 for token in tokenize.generate_tokens(self.readline_check_physical): |
1386 self.tokens.append(token) |
966 if options.verbose >= 3: |
1387 token_type, text = token[0:2] |
967 if token[2][0] == token[3][0]: |
1388 if self.verbose >= 3: |
968 pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) |
1389 if token[2][0] == token[3][0]: |
969 else: |
1390 pos = '[%s:%s]' % (token[2][1] or '', token[3][1]) |
970 pos = 'l.%s' % token[3][0] |
1391 else: |
971 print('l.%s\t%s\t%s\t%r' % |
1392 pos = 'l.%s' % token[3][0] |
972 (token[2][0], pos, tokenize.tok_name[token[0]], token[1])) |
1393 print('l.%s\t%s\t%s\t%r' % |
973 self.tokens.append(token) |
1394 (token[2][0], pos, tokenize.tok_name[token[0]], text)) |
974 token_type, text = token[0:2] |
1395 if token_type == tokenize.OP: |
975 if token_type == tokenize.OP and text in '([{': |
1396 if text in '([{': |
976 parens += 1 |
1397 parens += 1 |
977 if token_type == tokenize.OP and text in '}])': |
1398 elif text in '}])': |
978 parens -= 1 |
1399 parens -= 1 |
979 if token_type == tokenize.NEWLINE and not parens: |
1400 elif not parens: |
|
1401 if token_type == tokenize.NEWLINE: |
|
1402 if self.blank_lines < blank_lines_before_comment: |
|
1403 self.blank_lines = blank_lines_before_comment |
980 self.check_logical() |
1404 self.check_logical() |
981 self.blank_lines = 0 |
|
982 self.blank_lines_before_comment = 0 |
|
983 self.tokens = [] |
1405 self.tokens = [] |
984 if token_type == tokenize.NL and not parens: |
1406 self.blank_lines = blank_lines_before_comment = 0 |
985 if len(self.tokens) <= 1: |
1407 elif token_type == tokenize.NL: |
|
1408 if len(self.tokens) == 1: |
986 # The physical line contains only this token. |
1409 # The physical line contains only this token. |
987 self.blank_lines += 1 |
1410 self.blank_lines += 1 |
988 self.tokens = [] |
1411 self.tokens = [] |
989 if token_type == tokenize.COMMENT: |
1412 elif token_type == tokenize.COMMENT and len(self.tokens) == 1: |
990 source_line = token[4] |
1413 if blank_lines_before_comment < self.blank_lines: |
991 token_start = token[2][1] |
1414 blank_lines_before_comment = self.blank_lines |
992 if source_line[:token_start].strip() == '': |
1415 self.blank_lines = 0 |
993 self.blank_lines_before_comment = max(self.blank_lines, |
1416 if COMMENT_WITH_NL: |
994 self.blank_lines_before_comment) |
1417 # The comment also ends a physical line |
995 self.blank_lines = 0 |
|
996 if text.endswith('\n') and not parens: |
|
997 # The comment also ends a physical line. This works around |
|
998 # Python < 2.6 behaviour, which does not generate NL after |
|
999 # a comment which is on a line by itself. |
|
1000 self.tokens = [] |
1418 self.tokens = [] |
1001 except tokenize.TokenError, err: |
1419 return self.report.get_file_results() |
1002 msg, (lnum, pos) = err.args |
1420 |
1003 self.report_error_args(lnum, pos, "E901", "TokenError", msg) |
1421 |
|
1422 class BaseReport(object): |
|
1423 """Collect the results of the checks.""" |
|
1424 print_filename = False |
|
1425 |
|
1426 def __init__(self, options): |
|
1427 self._benchmark_keys = options.benchmark_keys |
|
1428 self._ignore_code = options.ignore_code |
|
1429 # Results |
|
1430 self.elapsed = 0 |
|
1431 self.total_errors = 0 |
|
1432 self.counters = dict.fromkeys(self._benchmark_keys, 0) |
|
1433 self.messages = {} |
|
1434 |
|
1435 def start(self): |
|
1436 """Start the timer.""" |
|
1437 self._start_time = time.time() |
|
1438 |
|
1439 def stop(self): |
|
1440 """Stop the timer.""" |
|
1441 self.elapsed = time.time() - self._start_time |
|
1442 |
|
1443 def init_file(self, filename, lines, expected, line_offset): |
|
1444 """Signal a new file.""" |
|
1445 self.filename = filename |
|
1446 self.lines = lines |
|
1447 self.expected = expected or () |
|
1448 self.line_offset = line_offset |
|
1449 self.file_errors = 0 |
|
1450 self.counters['files'] += 1 |
|
1451 self.counters['physical lines'] += len(lines) |
|
1452 |
|
1453 def increment_logical_line(self): |
|
1454 """Signal a new logical line.""" |
|
1455 self.counters['logical lines'] += 1 |
|
1456 |
|
1457 def error(self, line_number, offset, text, check): |
|
1458 """Report an error, according to options.""" |
|
1459 code = text[:4] |
|
1460 if self._ignore_code(code): |
|
1461 return |
|
1462 if code in self.counters: |
|
1463 self.counters[code] += 1 |
|
1464 else: |
|
1465 self.counters[code] = 1 |
|
1466 self.messages[code] = text[5:] |
|
1467 # Don't care about expected errors or warnings |
|
1468 if code in self.expected: |
|
1469 return |
|
1470 if self.print_filename and not self.file_errors: |
|
1471 print(self.filename) |
|
1472 self.file_errors += 1 |
|
1473 self.total_errors += 1 |
|
1474 return code |
|
1475 |
|
1476 def error_args(self, line_number, offset, code, check, *args): |
|
1477 """Report an error, according to options.""" |
|
1478 if self._ignore_code(code): |
|
1479 return |
|
1480 if code in self.counters: |
|
1481 self.counters[code] += 1 |
|
1482 else: |
|
1483 self.counters[code] = 1 |
|
1484 # Don't care about expected errors or warnings |
|
1485 if code in self.expected: |
|
1486 return |
|
1487 if self.print_filename and not self.file_errors: |
|
1488 print(self.filename) |
|
1489 self.file_errors += 1 |
|
1490 self.total_errors += 1 |
|
1491 return code |
|
1492 |
|
1493 def get_file_results(self): |
|
1494 """Return the count of errors and warnings for this file.""" |
1004 return self.file_errors |
1495 return self.file_errors |
1005 |
1496 |
1006 def report_error(self, line_number, offset, text, check): |
1497 def get_count(self, prefix=''): |
1007 """ |
1498 """Return the total count of errors and warnings.""" |
1008 Report an error, according to options. |
1499 return sum([self.counters[key] |
1009 """ |
1500 for key in self.messages if key.startswith(prefix)]) |
1010 code = text[:4] |
1501 |
1011 if ignore_code(code): |
1502 def get_statistics(self, prefix=''): |
|
1503 """ |
|
1504 Get statistics for message codes that start with the prefix. |
|
1505 |
|
1506 prefix='' matches all errors and warnings |
|
1507 prefix='E' matches all errors |
|
1508 prefix='W' matches all warnings |
|
1509 prefix='E4' matches all errors that have to do with imports |
|
1510 """ |
|
1511 return ['%-7s %s %s' % (self.counters[key], key, self.messages[key]) |
|
1512 for key in sorted(self.messages) if key.startswith(prefix)] |
|
1513 |
|
1514 def print_statistics(self, prefix=''): |
|
1515 """Print overall statistics (number of errors and warnings).""" |
|
1516 for line in self.get_statistics(prefix): |
|
1517 print(line) |
|
1518 |
|
1519 def print_benchmark(self): |
|
1520 """Print benchmark numbers.""" |
|
1521 print('%-7.2f %s' % (self.elapsed, 'seconds elapsed')) |
|
1522 if self.elapsed: |
|
1523 for key in self._benchmark_keys: |
|
1524 print('%-7d %s per second (%d total)' % |
|
1525 (self.counters[key] / self.elapsed, key, |
|
1526 self.counters[key])) |
|
1527 |
|
1528 |
|
1529 class FileReport(BaseReport): |
|
1530 """Collect the results of the checks and print only the filenames.""" |
|
1531 print_filename = True |
|
1532 |
|
1533 |
|
1534 class StandardReport(BaseReport): |
|
1535 """Collect and print the results of the checks.""" |
|
1536 |
|
1537 def __init__(self, options): |
|
1538 super(StandardReport, self).__init__(options) |
|
1539 self._fmt = REPORT_FORMAT.get(options.format.lower(), |
|
1540 options.format) |
|
1541 self._repeat = options.repeat |
|
1542 self._show_source = options.show_source |
|
1543 self._show_pep8 = options.show_pep8 |
|
1544 |
|
1545 def init_file(self, filename, lines, expected, line_offset): |
|
1546 """Signal a new file.""" |
|
1547 self._deferred_print = [] |
|
1548 return super(StandardReport, self).init_file( |
|
1549 filename, lines, expected, line_offset) |
|
1550 |
|
1551 def error(self, line_number, offset, text, check): |
|
1552 """Report an error, according to options.""" |
|
1553 code = super(StandardReport, self).error(line_number, offset, |
|
1554 text, check) |
|
1555 if code and (self.counters[code] == 1 or self._repeat): |
|
1556 self._deferred_print.append( |
|
1557 (line_number, offset, code, text[5:], check.__doc__)) |
|
1558 return code |
|
1559 |
|
1560 def error_args(self, line_number, offset, code, check, *args): |
|
1561 """Report an error, according to options.""" |
|
1562 code = super(StandardReport, self).error_args(line_number, offset, |
|
1563 code, check, *args) |
|
1564 if code and (self.counters[code] == 1 or self._repeat): |
|
1565 self._deferred_print.append( |
|
1566 (line_number, offset, code, "", check.__doc__)) |
|
1567 return code |
|
1568 |
|
1569 def get_file_results(self): |
|
1570 """Print the result and return the overall count for this file.""" |
|
1571 self._deferred_print.sort() |
|
1572 for line_number, offset, code, text, doc in self._deferred_print: |
|
1573 print(self._fmt % { |
|
1574 'path': self.filename, |
|
1575 'row': self.line_offset + line_number, 'col': offset + 1, |
|
1576 'code': code, 'text': text, |
|
1577 }) |
|
1578 if self._show_source: |
|
1579 if line_number > len(self.lines): |
|
1580 line = '' |
|
1581 else: |
|
1582 line = self.lines[line_number - 1] |
|
1583 print(line.rstrip()) |
|
1584 print(' ' * offset + '^') |
|
1585 if self._show_pep8 and doc: |
|
1586 print(doc.lstrip('\n').rstrip()) |
|
1587 return self.file_errors |
|
1588 |
|
1589 |
|
1590 class DiffReport(StandardReport): |
|
1591 """Collect and print the results for the changed lines only.""" |
|
1592 |
|
1593 def __init__(self, options): |
|
1594 super(DiffReport, self).__init__(options) |
|
1595 self._selected = options.selected_lines |
|
1596 |
|
1597 def error(self, line_number, offset, text, check): |
|
1598 if line_number not in self._selected[self.filename]: |
1012 return |
1599 return |
1013 if options.quiet == 1 and not self.file_errors: |
1600 return super(DiffReport, self).error(line_number, offset, text, check) |
1014 message(self.filename) |
1601 |
1015 if code in options.counters: |
1602 |
1016 options.counters[code] += 1 |
1603 class StyleGuide(object): |
|
1604 """Initialize a PEP-8 instance with few options.""" |
|
1605 |
|
1606 def __init__(self, *args, **kwargs): |
|
1607 # build options from the command line |
|
1608 self.checker_class = kwargs.pop('checker_class', Checker) |
|
1609 parse_argv = kwargs.pop('parse_argv', False) |
|
1610 config_file = kwargs.pop('config_file', None) |
|
1611 parser = kwargs.pop('parser', None) |
|
1612 options, self.paths = process_options( |
|
1613 parse_argv=parse_argv, config_file=config_file, parser=parser) |
|
1614 if args or kwargs: |
|
1615 # build options from dict |
|
1616 options_dict = dict(*args, **kwargs) |
|
1617 options.__dict__.update(options_dict) |
|
1618 if 'paths' in options_dict: |
|
1619 self.paths = options_dict['paths'] |
|
1620 |
|
1621 self.runner = self.input_file |
|
1622 self.options = options |
|
1623 |
|
1624 if not options.reporter: |
|
1625 options.reporter = BaseReport if options.quiet else StandardReport |
|
1626 |
|
1627 for index, value in enumerate(options.exclude): |
|
1628 options.exclude[index] = value.rstrip('/') |
|
1629 options.select = tuple(options.select or ()) |
|
1630 if not (options.select or options.ignore or |
|
1631 options.testsuite or options.doctest) and DEFAULT_IGNORE: |
|
1632 # The default choice: ignore controversial checks |
|
1633 options.ignore = tuple(DEFAULT_IGNORE.split(',')) |
1017 else: |
1634 else: |
1018 options.counters[code] = 1 |
1635 # Ignore all checks which are not explicitly selected |
1019 options.messages[code] = text[5:] |
1636 options.ignore = ('',) if options.select else tuple(options.ignore) |
1020 if options.quiet or code in self.expected: |
1637 options.benchmark_keys = BENCHMARK_KEYS[:] |
1021 # Don't care about expected errors or warnings |
1638 options.ignore_code = self.ignore_code |
1022 return |
1639 options.physical_checks = self.get_checks('physical_line') |
1023 self.file_errors += 1 |
1640 options.logical_checks = self.get_checks('logical_line') |
1024 if options.counters[code] == 1 or options.repeat: |
1641 options.ast_checks = self.get_checks('tree') |
1025 message("%s:%s:%d: %s" % |
1642 self.init_report() |
1026 (self.filename, self.line_offset + line_number, |
1643 |
1027 offset + 1, text)) |
1644 def init_report(self, reporter=None): |
1028 if options.show_source: |
1645 """Initialize the report instance.""" |
1029 line = self.lines[line_number - 1] |
1646 self.options.report = (reporter or self.options.reporter)(self.options) |
1030 message(line.rstrip()) |
1647 return self.options.report |
1031 message(' ' * offset + '^') |
1648 |
1032 if options.show_pep8: |
1649 def check_files(self, paths=None): |
1033 message(check.__doc__.lstrip('\n').rstrip()) |
1650 """Run all checks on the paths.""" |
1034 |
1651 if paths is None: |
1035 |
1652 paths = self.paths |
1036 def input_file(filename): |
1653 report = self.options.report |
1037 """ |
1654 runner = self.runner |
1038 Run all checks on a Python source file. |
1655 report.start() |
1039 """ |
1656 try: |
1040 if options.verbose: |
1657 for path in paths: |
1041 message('checking ' + filename) |
1658 if os.path.isdir(path): |
1042 Checker(filename).check_all() |
1659 self.input_dir(path) |
1043 |
1660 elif not self.excluded(path): |
1044 |
1661 runner(path) |
1045 def input_dir(dirname, runner=None): |
1662 except KeyboardInterrupt: |
1046 """ |
1663 print('... stopped') |
1047 Check all Python source files in this directory and all subdirectories. |
1664 report.stop() |
1048 """ |
1665 return report |
1049 dirname = dirname.rstrip('/') |
1666 |
1050 if excluded(dirname): |
1667 def input_file(self, filename, lines=None, expected=None, line_offset=0): |
1051 return |
1668 """Run all checks on a Python source file.""" |
1052 if runner is None: |
1669 if self.options.verbose: |
1053 runner = input_file |
1670 print('checking %s' % filename) |
1054 for root, dirs, files in os.walk(dirname): |
1671 fchecker = self.checker_class( |
1055 if options.verbose: |
1672 filename, lines=lines, options=self.options) |
1056 message('directory ' + root) |
1673 return fchecker.check_all(expected=expected, line_offset=line_offset) |
1057 options.counters['directories'] += 1 |
1674 |
1058 dirs.sort() |
1675 def input_dir(self, dirname): |
1059 for subdir in dirs: |
1676 """Check all files in this directory and all subdirectories.""" |
1060 if excluded(subdir): |
1677 dirname = dirname.rstrip('/') |
1061 dirs.remove(subdir) |
1678 if self.excluded(dirname): |
1062 files.sort() |
1679 return 0 |
1063 for filename in files: |
1680 counters = self.options.report.counters |
1064 if filename_match(filename) and not excluded(filename): |
1681 verbose = self.options.verbose |
1065 options.counters['files'] += 1 |
1682 filepatterns = self.options.filename |
1066 runner(os.path.join(root, filename)) |
1683 runner = self.runner |
1067 |
1684 for root, dirs, files in os.walk(dirname): |
1068 |
1685 if verbose: |
1069 def excluded(filename): |
1686 print('directory ' + root) |
1070 """ |
1687 counters['directories'] += 1 |
1071 Check if options.exclude contains a pattern that matches filename. |
1688 for subdir in sorted(dirs): |
1072 """ |
1689 if self.excluded(subdir, root): |
1073 basename = os.path.basename(filename) |
1690 dirs.remove(subdir) |
1074 for pattern in options.exclude: |
1691 for filename in sorted(files): |
1075 if fnmatch(basename, pattern): |
1692 # contain a pattern that matches? |
1076 # print basename, 'excluded because it matches', pattern |
1693 if ((filename_match(filename, filepatterns) and |
|
1694 not self.excluded(filename, root))): |
|
1695 runner(os.path.join(root, filename)) |
|
1696 |
|
1697 def excluded(self, filename, parent=None): |
|
1698 """ |
|
1699 Check if options.exclude contains a pattern that matches filename. |
|
1700 """ |
|
1701 if not self.options.exclude: |
|
1702 return False |
|
1703 basename = os.path.basename(filename) |
|
1704 if filename_match(basename, self.options.exclude): |
1077 return True |
1705 return True |
1078 |
1706 if parent: |
1079 |
1707 filename = os.path.join(parent, filename) |
1080 def filename_match(filename): |
1708 return filename_match(filename, self.options.exclude) |
1081 """ |
1709 |
1082 Check if options.filename contains a pattern that matches filename. |
1710 def ignore_code(self, code): |
1083 If options.filename is unspecified, this always returns True. |
1711 """ |
1084 """ |
1712 Check if the error code should be ignored. |
1085 if not options.filename: |
1713 |
1086 return True |
1714 If 'options.select' contains a prefix of the error code, |
1087 for pattern in options.filename: |
1715 return False. Else, if 'options.ignore' contains a prefix of |
1088 if fnmatch(filename, pattern): |
1716 the error code, return True. |
1089 return True |
1717 """ |
1090 |
1718 return (code.startswith(self.options.ignore) and |
1091 |
1719 not code.startswith(self.options.select)) |
1092 def ignore_code(code): |
1720 |
1093 """ |
1721 def get_checks(self, argument_name): |
1094 Check if options.ignore contains a prefix of the error code. |
1722 """ |
1095 If options.select contains a prefix of the error code, do not ignore it. |
1723 Find all globally visible functions where the first argument name |
1096 """ |
1724 starts with argument_name and which contain selected tests. |
1097 for select in options.select: |
1725 """ |
1098 if code.startswith(select): |
1726 checks = [] |
1099 return False |
1727 for check, attrs in _checks[argument_name].items(): |
1100 for ignore in options.ignore: |
1728 (codes, args) = attrs |
1101 if code.startswith(ignore): |
1729 if any(not (code and self.ignore_code(code)) for code in codes): |
1102 return True |
1730 checks.append((check.__name__, check, args)) |
1103 |
1731 return sorted(checks) |
1104 |
1732 |
1105 def reset_counters(): |
1733 |
1106 for key in list(options.counters.keys()): |
1734 def get_parser(prog='pep8', version=__version__): |
1107 if key not in BENCHMARK_KEYS: |
1735 parser = OptionParser(prog=prog, version=version, |
1108 del options.counters[key] |
|
1109 options.messages = {} |
|
1110 |
|
1111 |
|
1112 def get_error_statistics(): |
|
1113 """Get error statistics.""" |
|
1114 return get_statistics("E") |
|
1115 |
|
1116 |
|
1117 def get_warning_statistics(): |
|
1118 """Get warning statistics.""" |
|
1119 return get_statistics("W") |
|
1120 |
|
1121 |
|
1122 def get_statistics(prefix=''): |
|
1123 """ |
|
1124 Get statistics for message codes that start with the prefix. |
|
1125 |
|
1126 prefix='' matches all errors and warnings |
|
1127 prefix='E' matches all errors |
|
1128 prefix='W' matches all warnings |
|
1129 prefix='E4' matches all errors that have to do with imports |
|
1130 """ |
|
1131 stats = [] |
|
1132 keys = list(options.messages.keys()) |
|
1133 keys.sort() |
|
1134 for key in keys: |
|
1135 if key.startswith(prefix): |
|
1136 stats.append('%-7s %s %s' % |
|
1137 (options.counters[key], key, options.messages[key])) |
|
1138 return stats |
|
1139 |
|
1140 |
|
1141 def get_count(prefix=''): |
|
1142 """Return the total count of errors and warnings.""" |
|
1143 keys = list(options.messages.keys()) |
|
1144 count = 0 |
|
1145 for key in keys: |
|
1146 if key.startswith(prefix): |
|
1147 count += options.counters[key] |
|
1148 return count |
|
1149 |
|
1150 |
|
1151 def print_statistics(prefix=''): |
|
1152 """Print overall statistics (number of errors and warnings).""" |
|
1153 for line in get_statistics(prefix): |
|
1154 print(line) |
|
1155 |
|
1156 |
|
1157 def print_benchmark(elapsed): |
|
1158 """ |
|
1159 Print benchmark numbers. |
|
1160 """ |
|
1161 print('%-7.2f %s' % (elapsed, 'seconds elapsed')) |
|
1162 for key in BENCHMARK_KEYS: |
|
1163 print('%-7d %s per second (%d total)' % ( |
|
1164 options.counters[key] / elapsed, key, |
|
1165 options.counters[key])) |
|
1166 |
|
1167 |
|
1168 def run_tests(filename): |
|
1169 """ |
|
1170 Run all the tests from a file. |
|
1171 |
|
1172 A test file can provide many tests. Each test starts with a declaration. |
|
1173 This declaration is a single line starting with '#:'. |
|
1174 It declares codes of expected failures, separated by spaces or 'Okay' |
|
1175 if no failure is expected. |
|
1176 If the file does not contain such declaration, it should pass all tests. |
|
1177 If the declaration is empty, following lines are not checked, until next |
|
1178 declaration. |
|
1179 |
|
1180 Examples: |
|
1181 |
|
1182 * Only E224 and W701 are expected: #: E224 W701 |
|
1183 * Following example is conform: #: Okay |
|
1184 * Don't check these lines: #: |
|
1185 """ |
|
1186 lines = readlines(filename) + ['#:\n'] |
|
1187 line_offset = 0 |
|
1188 codes = ['Okay'] |
|
1189 testcase = [] |
|
1190 for index, line in enumerate(lines): |
|
1191 if not line.startswith('#:'): |
|
1192 if codes: |
|
1193 # Collect the lines of the test case |
|
1194 testcase.append(line) |
|
1195 continue |
|
1196 if codes and index > 0: |
|
1197 label = '%s:%s:1' % (filename, line_offset + 1) |
|
1198 codes = [c for c in codes if c != 'Okay'] |
|
1199 # Run the checker |
|
1200 errors = Checker(filename, testcase).check_all(codes, line_offset) |
|
1201 # Check if the expected errors were found |
|
1202 for code in codes: |
|
1203 if not options.counters.get(code): |
|
1204 errors += 1 |
|
1205 message('%s: error %s not found' % (label, code)) |
|
1206 if options.verbose and not errors: |
|
1207 message('%s: passed (%s)' % (label, ' '.join(codes))) |
|
1208 # Keep showing errors for multiple tests |
|
1209 reset_counters() |
|
1210 # output the real line numbers |
|
1211 line_offset = index |
|
1212 # configure the expected errors |
|
1213 codes = line.split()[1:] |
|
1214 # empty the test case buffer |
|
1215 del testcase[:] |
|
1216 |
|
1217 |
|
1218 def selftest(): |
|
1219 """ |
|
1220 Test all check functions with test cases in docstrings. |
|
1221 """ |
|
1222 count_passed = 0 |
|
1223 count_failed = 0 |
|
1224 checks = options.physical_checks + options.logical_checks |
|
1225 for name, check, argument_names in checks: |
|
1226 for line in check.__doc__.splitlines(): |
|
1227 line = line.lstrip() |
|
1228 match = SELFTEST_REGEX.match(line) |
|
1229 if match is None: |
|
1230 continue |
|
1231 code, source = match.groups() |
|
1232 checker = Checker(None) |
|
1233 for part in source.split(r'\n'): |
|
1234 part = part.replace(r'\t', '\t') |
|
1235 part = part.replace(r'\s', ' ') |
|
1236 checker.lines.append(part + '\n') |
|
1237 options.quiet = 2 |
|
1238 checker.check_all() |
|
1239 error = None |
|
1240 if code == 'Okay': |
|
1241 if len(options.counters) > len(BENCHMARK_KEYS): |
|
1242 codes = [key for key in options.counters.keys() |
|
1243 if key not in BENCHMARK_KEYS] |
|
1244 error = "incorrectly found %s" % ', '.join(codes) |
|
1245 elif not options.counters.get(code): |
|
1246 error = "failed to find %s" % code |
|
1247 # Reset the counters |
|
1248 reset_counters() |
|
1249 if not error: |
|
1250 count_passed += 1 |
|
1251 else: |
|
1252 count_failed += 1 |
|
1253 if len(checker.lines) == 1: |
|
1254 print("pep8.py: %s: %s" % |
|
1255 (error, checker.lines[0].rstrip())) |
|
1256 else: |
|
1257 print("pep8.py: %s:" % error) |
|
1258 for line in checker.lines: |
|
1259 print(line.rstrip()) |
|
1260 if options.verbose: |
|
1261 print("%d passed and %d failed." % (count_passed, count_failed)) |
|
1262 if count_failed: |
|
1263 print("Test failed.") |
|
1264 else: |
|
1265 print("Test passed.") |
|
1266 |
|
1267 |
|
1268 def process_options(arglist=None): |
|
1269 """ |
|
1270 Process options passed either via arglist or via command line args. |
|
1271 """ |
|
1272 global options, args |
|
1273 parser = OptionParser(version=__version__, |
|
1274 usage="%prog [options] input ...") |
1736 usage="%prog [options] input ...") |
|
1737 parser.config_options = [ |
|
1738 'exclude', 'filename', 'select', 'ignore', 'max-line-length', |
|
1739 'hang-closing', 'count', 'format', 'quiet', 'show-pep8', |
|
1740 'show-source', 'statistics', 'verbose'] |
1275 parser.add_option('-v', '--verbose', default=0, action='count', |
1741 parser.add_option('-v', '--verbose', default=0, action='count', |
1276 help="print status messages, or debug with -vv") |
1742 help="print status messages, or debug with -vv") |
1277 parser.add_option('-q', '--quiet', default=0, action='count', |
1743 parser.add_option('-q', '--quiet', default=0, action='count', |
1278 help="report only file names, or nothing with -qq") |
1744 help="report only file names, or nothing with -qq") |
1279 parser.add_option('-r', '--repeat', action='store_true', |
1745 parser.add_option('-r', '--repeat', default=True, action='store_true', |
1280 help="show all occurrences of the same error") |
1746 help="(obsolete) show all occurrences of the same error") |
|
1747 parser.add_option('--first', action='store_false', dest='repeat', |
|
1748 help="show first occurrence of each error") |
1281 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, |
1749 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE, |
1282 help="exclude files or directories which match these " |
1750 help="exclude files or directories which match these " |
1283 "comma separated patterns (default: %s)" % |
1751 "comma separated patterns (default: %default)") |
1284 DEFAULT_EXCLUDE) |
|
1285 parser.add_option('--filename', metavar='patterns', default='*.py', |
1752 parser.add_option('--filename', metavar='patterns', default='*.py', |
1286 help="when parsing directories, only check filenames " |
1753 help="when parsing directories, only check filenames " |
1287 "matching these comma separated patterns (default: " |
1754 "matching these comma separated patterns " |
1288 "*.py)") |
1755 "(default: %default)") |
1289 parser.add_option('--select', metavar='errors', default='', |
1756 parser.add_option('--select', metavar='errors', default='', |
1290 help="select errors and warnings (e.g. E,W6)") |
1757 help="select errors and warnings (e.g. E,W6)") |
1291 parser.add_option('--ignore', metavar='errors', default='', |
1758 parser.add_option('--ignore', metavar='errors', default='', |
1292 help="skip errors and warnings (e.g. E4,W)") |
1759 help="skip errors and warnings (e.g. E4,W)") |
1293 parser.add_option('--show-source', action='store_true', |
1760 parser.add_option('--show-source', action='store_true', |
1294 help="show source code for each error") |
1761 help="show source code for each error") |
1295 parser.add_option('--show-pep8', action='store_true', |
1762 parser.add_option('--show-pep8', action='store_true', |
1296 help="show text of PEP 8 for each error") |
1763 help="show text of PEP 8 for each error " |
|
1764 "(implies --first)") |
1297 parser.add_option('--statistics', action='store_true', |
1765 parser.add_option('--statistics', action='store_true', |
1298 help="count errors and warnings") |
1766 help="count errors and warnings") |
1299 parser.add_option('--count', action='store_true', |
1767 parser.add_option('--count', action='store_true', |
1300 help="print total number of errors and warnings " |
1768 help="print total number of errors and warnings " |
1301 "to standard error and set exit code to 1 if " |
1769 "to standard error and set exit code to 1 if " |
1302 "total is not null") |
1770 "total is not null") |
1303 parser.add_option('--benchmark', action='store_true', |
1771 parser.add_option('--max-line-length', type='int', metavar='n', |
1304 help="measure processing speed") |
1772 default=MAX_LINE_LENGTH, |
1305 parser.add_option('--testsuite', metavar='dir', |
1773 help="set maximum allowed line length " |
1306 help="run regression tests from dir") |
1774 "(default: %default)") |
1307 parser.add_option('--doctest', action='store_true', |
1775 parser.add_option('--hang-closing', action='store_true', |
1308 help="run doctest on myself") |
1776 help="hang closing bracket instead of matching " |
|
1777 "indentation of opening bracket's line") |
|
1778 parser.add_option('--format', metavar='format', default='default', |
|
1779 help="set the error format [default|pylint|<custom>]") |
|
1780 parser.add_option('--diff', action='store_true', |
|
1781 help="report only lines changed according to the " |
|
1782 "unified diff received on STDIN") |
|
1783 group = parser.add_option_group("Testing Options") |
|
1784 if os.path.exists(TESTSUITE_PATH): |
|
1785 group.add_option('--testsuite', metavar='dir', |
|
1786 help="run regression tests from dir") |
|
1787 group.add_option('--doctest', action='store_true', |
|
1788 help="run doctest on myself") |
|
1789 group.add_option('--benchmark', action='store_true', |
|
1790 help="measure processing speed") |
|
1791 return parser |
|
1792 |
|
1793 |
|
1794 def read_config(options, args, arglist, parser): |
|
1795 """Read both user configuration and local configuration.""" |
|
1796 config = RawConfigParser() |
|
1797 |
|
1798 user_conf = options.config |
|
1799 if user_conf and os.path.isfile(user_conf): |
|
1800 if options.verbose: |
|
1801 print('user configuration: %s' % user_conf) |
|
1802 config.read(user_conf) |
|
1803 |
|
1804 parent = tail = args and os.path.abspath(os.path.commonprefix(args)) |
|
1805 while tail: |
|
1806 if config.read([os.path.join(parent, fn) for fn in PROJECT_CONFIG]): |
|
1807 if options.verbose: |
|
1808 print('local configuration: in %s' % parent) |
|
1809 break |
|
1810 parent, tail = os.path.split(parent) |
|
1811 |
|
1812 pep8_section = parser.prog |
|
1813 if config.has_section(pep8_section): |
|
1814 option_list = dict([(o.dest, o.type or o.action) |
|
1815 for o in parser.option_list]) |
|
1816 |
|
1817 # First, read the default values |
|
1818 new_options, _ = parser.parse_args([]) |
|
1819 |
|
1820 # Second, parse the configuration |
|
1821 for opt in config.options(pep8_section): |
|
1822 if options.verbose > 1: |
|
1823 print(" %s = %s" % (opt, config.get(pep8_section, opt))) |
|
1824 if opt.replace('_', '-') not in parser.config_options: |
|
1825 print("Unknown option: '%s'\n not in [%s]" % |
|
1826 (opt, ' '.join(parser.config_options))) |
|
1827 sys.exit(1) |
|
1828 normalized_opt = opt.replace('-', '_') |
|
1829 opt_type = option_list[normalized_opt] |
|
1830 if opt_type in ('int', 'count'): |
|
1831 value = config.getint(pep8_section, opt) |
|
1832 elif opt_type == 'string': |
|
1833 value = config.get(pep8_section, opt) |
|
1834 else: |
|
1835 assert opt_type in ('store_true', 'store_false') |
|
1836 value = config.getboolean(pep8_section, opt) |
|
1837 setattr(new_options, normalized_opt, value) |
|
1838 |
|
1839 # Third, overwrite with the command-line options |
|
1840 options, _ = parser.parse_args(arglist, values=new_options) |
|
1841 options.doctest = options.testsuite = False |
|
1842 return options |
|
1843 |
|
1844 |
|
1845 def process_options(arglist=None, parse_argv=False, config_file=None, |
|
1846 parser=None): |
|
1847 """Process options passed either via arglist or via command line args.""" |
|
1848 if not arglist and not parse_argv: |
|
1849 # Don't read the command line if the module is used as a library. |
|
1850 arglist = [] |
|
1851 if not parser: |
|
1852 parser = get_parser() |
|
1853 if not parser.has_option('--config'): |
|
1854 if config_file is True: |
|
1855 config_file = DEFAULT_CONFIG |
|
1856 group = parser.add_option_group("Configuration", description=( |
|
1857 "The project options are read from the [%s] section of the " |
|
1858 "tox.ini file or the setup.cfg file located in any parent folder " |
|
1859 "of the path(s) being processed. Allowed options are: %s." % |
|
1860 (parser.prog, ', '.join(parser.config_options)))) |
|
1861 group.add_option('--config', metavar='path', default=config_file, |
|
1862 help="user config file location (default: %default)") |
1309 options, args = parser.parse_args(arglist) |
1863 options, args = parser.parse_args(arglist) |
1310 if options.testsuite: |
1864 options.reporter = None |
|
1865 |
|
1866 if options.ensure_value('testsuite', False): |
1311 args.append(options.testsuite) |
1867 args.append(options.testsuite) |
1312 if not args and not options.doctest: |
1868 elif not options.ensure_value('doctest', False): |
1313 parser.error('input not specified') |
1869 if parse_argv and not args: |
1314 options.prog = os.path.basename(sys.argv[0]) |
1870 if options.diff or any(os.path.exists(name) |
|
1871 for name in PROJECT_CONFIG): |
|
1872 args = ['.'] |
|
1873 else: |
|
1874 parser.error('input not specified') |
|
1875 options = read_config(options, args, arglist, parser) |
|
1876 options.reporter = parse_argv and options.quiet == 1 and FileReport |
|
1877 |
|
1878 options.filename = options.filename and options.filename.split(',') |
1315 options.exclude = options.exclude.split(',') |
1879 options.exclude = options.exclude.split(',') |
1316 for index in range(len(options.exclude)): |
1880 options.select = options.select and options.select.split(',') |
1317 options.exclude[index] = options.exclude[index].rstrip('/') |
1881 options.ignore = options.ignore and options.ignore.split(',') |
1318 if options.filename: |
1882 |
1319 options.filename = options.filename.split(',') |
1883 if options.diff: |
1320 if options.select: |
1884 options.reporter = DiffReport |
1321 options.select = options.select.split(',') |
1885 stdin = stdin_get_value() |
|
1886 options.selected_lines = parse_udiff(stdin, options.filename, args[0]) |
|
1887 args = sorted(options.selected_lines) |
|
1888 |
|
1889 return options, args |
|
1890 |
|
1891 |
|
1892 def _main(): |
|
1893 """Parse options and run checks on Python source.""" |
|
1894 pep8style = StyleGuide(parse_argv=True, config_file=True) |
|
1895 options = pep8style.options |
|
1896 if options.doctest or options.testsuite: |
|
1897 from testsuite.support import run_tests |
|
1898 report = run_tests(pep8style) |
1322 else: |
1899 else: |
1323 options.select = [] |
1900 report = pep8style.check_files() |
1324 if options.ignore: |
|
1325 options.ignore = options.ignore.split(',') |
|
1326 elif options.select: |
|
1327 # Ignore all checks which are not explicitly selected |
|
1328 options.ignore = [''] |
|
1329 elif options.testsuite or options.doctest: |
|
1330 # For doctest and testsuite, all checks are required |
|
1331 options.ignore = [] |
|
1332 else: |
|
1333 # The default choice: ignore controversial checks |
|
1334 options.ignore = DEFAULT_IGNORE.split(',') |
|
1335 options.physical_checks = find_checks('physical_line') |
|
1336 options.logical_checks = find_checks('logical_line') |
|
1337 options.counters = dict.fromkeys(BENCHMARK_KEYS, 0) |
|
1338 options.messages = {} |
|
1339 return options, args |
|
1340 |
|
1341 |
|
1342 def _main(): |
|
1343 """ |
|
1344 Parse options and run checks on Python source. |
|
1345 """ |
|
1346 options, args = process_options() |
|
1347 if options.doctest: |
|
1348 import doctest |
|
1349 doctest.testmod(verbose=options.verbose) |
|
1350 selftest() |
|
1351 if options.testsuite: |
|
1352 runner = run_tests |
|
1353 else: |
|
1354 runner = input_file |
|
1355 start_time = time.time() |
|
1356 for path in args: |
|
1357 if os.path.isdir(path): |
|
1358 input_dir(path, runner=runner) |
|
1359 elif not excluded(path): |
|
1360 options.counters['files'] += 1 |
|
1361 runner(path) |
|
1362 elapsed = time.time() - start_time |
|
1363 if options.statistics: |
1901 if options.statistics: |
1364 print_statistics() |
1902 report.print_statistics() |
1365 if options.benchmark: |
1903 if options.benchmark: |
1366 print_benchmark(elapsed) |
1904 report.print_benchmark() |
1367 count = get_count() |
1905 if options.testsuite and not options.quiet: |
1368 if count: |
1906 report.print_results() |
|
1907 if report.total_errors: |
1369 if options.count: |
1908 if options.count: |
1370 sys.stderr.write(str(count) + '\n') |
1909 sys.stderr.write(str(report.total_errors) + '\n') |
1371 sys.exit(1) |
1910 sys.exit(1) |
1372 |
|
1373 |
1911 |
1374 if __name__ == '__main__': |
1912 if __name__ == '__main__': |
1375 _main() |
1913 _main() |
1376 |
1914 |
1377 # |
1915 # |