Update pep8.py to be far closer to upstream.

I also modified the hash-bang in flake8/flake8 and fixed check_code in
flake8/run.py
This commit is contained in:
Ian Cordasco 2012-12-21 17:44:30 -05:00
parent 2ebc38869a
commit e2e6337a29
3 changed files with 83 additions and 48 deletions

View file

@ -1,4 +1,4 @@
#!/home/tarek/dev/bitbucket.org/flake8-clean/bin/python #!/usr/bin/env python
from flake8.run import main from flake8.run import main
if __name__ == '__main__': if __name__ == '__main__':

127
flake8/pep8.py Normal file → Executable file
View file

@ -1,7 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# flake8: noqa # flake8: noqa
# pep8.py - Check Python source code formatting, according to PEP 8 # pep8.py - Check Python source code formatting, according to PEP 8
# Copyright (C) 2006 Johann C. Rocholl <johann@rocholl.net> # Copyright (C) 2006-2009 Johann C. Rocholl <johann@rocholl.net>
# Copyright (C) 2009-2012 Florent Xicluna <florent.xicluna@gmail.com>
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files # obtaining a copy of this software and associated documentation files
@ -89,14 +90,12 @@ These examples are verified automatically when pep8.py is run with the
--doctest option. You can add examples for your own check functions. --doctest option. You can add examples for your own check functions.
The format is simple: "Okay" or error/warning code followed by colon The format is simple: "Okay" or error/warning code followed by colon
and space, the rest of the line is example source code. If you put 'r' and space, the rest of the line is example source code. If you put 'r'
before the docstring, you can use \n for newline, \t for tab and \s before the docstring, you can use \n for newline and \t for tab.
for space.
""" """
from flake8.pyflakes import __version__ as pyflakes_version from flake8.pyflakes import __version__ as pyflakes_version
from flake8 import __version__ as flake8_version from flake8 import __version__ as flake8_version
__version__ = '1.3.4a0' __version__ = '1.3.5a'
import os import os
import sys import sys
@ -114,7 +113,7 @@ except ImportError:
from ConfigParser import RawConfigParser from ConfigParser import RawConfigParser
DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git'
DEFAULT_IGNORE = 'E24' DEFAULT_IGNORE = 'E226,E24'
if sys.platform == 'win32': if sys.platform == 'win32':
DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8')
else: else:
@ -135,14 +134,18 @@ BINARY_OPERATORS = frozenset([
'%', '^', '&', '|', '=', '/', '//', '<', '>', '<<']) '%', '^', '&', '|', '=', '/', '//', '<', '>', '<<'])
UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS
WS_OPTIONAL_OPERATORS = frozenset(['**', '*', '/', '//', '+', '-'])
WS_NEEDED_OPERATORS = frozenset([
'**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>',
'%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=',
'%', '^', '&', '|', '=', '<', '>', '<<'])
WHITESPACE = frozenset(' \t') WHITESPACE = frozenset(' \t')
SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE,
tokenize.INDENT, tokenize.DEDENT]) tokenize.INDENT, tokenize.DEDENT])
BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines'] BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines']
INDENT_REGEX = re.compile(r'([ \t]*)') INDENT_REGEX = re.compile(r'([ \t]*)')
RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)') RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,(.*)')
RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+')
SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)') SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)')
ERRORCODE_REGEX = re.compile(r'[EW]\d{3}') ERRORCODE_REGEX = re.compile(r'[EW]\d{3}')
DOCSTRING_REGEX = re.compile(r'u?r?["\']') DOCSTRING_REGEX = re.compile(r'u?r?["\']')
@ -151,11 +154,11 @@ WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)') COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)')
COMPARE_TYPE_REGEX = re.compile(r'([=!]=|is|is\s+not)\s*type(?:s\.(\w+)Type' COMPARE_TYPE_REGEX = re.compile(r'([=!]=|is|is\s+not)\s*type(?:s\.(\w+)Type'
r'|\(\s*(\(\s*\)|[^)]*[^ )])\s*\))') r'|\(\s*(\(\s*\)|[^)]*[^ )])\s*\))')
KEYWORD_REGEX = re.compile(r'(?:[^\s])(\s*)\b(?:%s)\b(\s*)' % KEYWORD_REGEX = re.compile(r'(?:[^\s]|\b)(\s*)\b(?:%s)\b(\s*)' %
r'|'.join(KEYWORDS)) r'|'.join(KEYWORDS))
OPERATOR_REGEX = re.compile(r'(?:[^\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)')
LAMBDA_REGEX = re.compile(r'\blambda\b') LAMBDA_REGEX = re.compile(r'\blambda\b')
HUNK_REGEX = re.compile(r'^@@ -\d+,\d+ \+(\d+),(\d+) @@.*$') HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
# Work around Python < 2.6 behaviour, which does not generate NL after # Work around Python < 2.6 behaviour, which does not generate NL after
# a comment which is on a line by itself. # a comment which is on a line by itself.
@ -215,8 +218,8 @@ def trailing_whitespace(physical_line):
The warning returned varies on whether the line itself is blank, for easier The warning returned varies on whether the line itself is blank, for easier
filtering for those who want to indent their blank lines. filtering for those who want to indent their blank lines.
Okay: spam(1) Okay: spam(1)\n#
W291: spam(1)\s W291: spam(1) \n#
W293: class Foo(object):\n \n bang = 12 W293: class Foo(object):\n \n bang = 12
""" """
physical_line = physical_line.rstrip('\n') # chr(10), newline physical_line = physical_line.rstrip('\n') # chr(10), newline
@ -392,13 +395,15 @@ def missing_whitespace(logical_line):
Okay: a[1:4:2] Okay: a[1:4:2]
E231: ['a','b'] E231: ['a','b']
E231: foo(bar,baz) E231: foo(bar,baz)
E231: [{'a':'b'}]
""" """
line = logical_line line = logical_line
for index in range(len(line) - 1): for index in range(len(line) - 1):
char = line[index] char = line[index]
if char in ',;:' and line[index + 1] not in WHITESPACE: if char in ',;:' and line[index + 1] not in WHITESPACE:
before = line[:index] before = line[:index]
if char == ':' and before.count('[') > before.count(']'): if char == ':' and before.count('[') > before.count(']') and \
before.rfind('{') < before.rfind('['):
continue # Slice syntax, no space required continue # Slice syntax, no space required
if char == ',' and line[index + 1] == ')': if char == ',' and line[index + 1] == ')':
continue # Allow tuple with only one element: (3,) continue # Allow tuple with only one element: (3,)
@ -555,7 +560,8 @@ def continuation_line_indentation(logical_line, tokens, indent_level, verbose):
if verbose >= 4: if verbose >= 4:
print("bracket depth %s indent to %s" % (depth, start[1])) print("bracket depth %s indent to %s" % (depth, start[1]))
# deal with implicit string concatenation # deal with implicit string concatenation
elif token_type == tokenize.STRING or text in ('u', 'ur', 'b', 'br'): elif (token_type in (tokenize.STRING, tokenize.COMMENT) or
text in ('u', 'ur', 'b', 'br')):
indent_chances[start[1]] = str indent_chances[start[1]] = str
# keep track of bracket depth # keep track of bracket depth
@ -679,14 +685,19 @@ def missing_whitespace_around_operator(logical_line, tokens):
Okay: alpha[:-i] Okay: alpha[:-i]
Okay: if not -5 < x < +5:\n pass Okay: if not -5 < x < +5:\n pass
Okay: lambda *args, **kw: (args, kw) Okay: lambda *args, **kw: (args, kw)
Okay: z = 2 ** 30
Okay: x = x / 2 - 1
E225: i=i+1 E225: i=i+1
E225: submitted +=1 E225: submitted +=1
E225: x = x*2 - 1
E225: hypot2 = x*x + y*y
E225: c = (a+b) * (a-b)
E225: c = alpha -4 E225: c = alpha -4
E225: x = x /2 - 1
E225: z = x **y E225: z = x **y
E226: c = (a+b) * (a-b)
E226: z = 2**30
E226: x = x*2 - 1
E226: x = x/2 - 1
E226: hypot2 = x*x + y*y
""" """
parens = 0 parens = 0
need_space = False need_space = False
@ -694,7 +705,7 @@ def missing_whitespace_around_operator(logical_line, tokens):
prev_text = prev_end = None prev_text = prev_end = None
for token_type, text, start, end, line in tokens: for token_type, text, start, end, line in tokens:
if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN):
# ERRORTOKEN is triggered by backticks in Python 3000 # ERRORTOKEN is triggered by backticks in Python 3
continue continue
if text in ('(', 'lambda'): if text in ('(', 'lambda'):
parens += 1 parens += 1
@ -702,32 +713,54 @@ def missing_whitespace_around_operator(logical_line, tokens):
parens -= 1 parens -= 1
if need_space: if need_space:
if start != prev_end: if start != prev_end:
# Found a (probably) needed space
if need_space is not True and not need_space[1]:
yield (need_space[0],
"E225 missing whitespace around operator")
need_space = False need_space = False
elif text == '>' and prev_text in ('<', '-'): elif text == '>' and prev_text in ('<', '-'):
# Tolerate the "<>" operator, even if running Python 3 # Tolerate the "<>" operator, even if running Python 3
# Deal with Python 3's annotated return value "->" # Deal with Python 3's annotated return value "->"
pass pass
else: else:
yield prev_end, "E225 missing whitespace around operator" if need_space is True or need_space[1]:
# A needed trailing space was not found
yield prev_end, "E225 missing whitespace around operator"
else:
yield (need_space[0],
"E226 missing optional whitespace around operator")
need_space = False need_space = False
elif token_type == tokenize.OP and prev_end is not None: elif token_type == tokenize.OP and prev_end is not None:
if text == '=' and parens: if text == '=' and parens:
# Allow keyword args or defaults: foo(bar=None). # Allow keyword args or defaults: foo(bar=None).
pass pass
elif text in BINARY_OPERATORS: elif text in WS_NEEDED_OPERATORS:
need_space = True need_space = True
elif text in UNARY_OPERATORS: elif text in UNARY_OPERATORS:
# Check if the operator is being used as a binary operator
# Allow unary operators: -123, -x, +1. # Allow unary operators: -123, -x, +1.
# Allow argument unpacking: foo(*args, **kwargs). # Allow argument unpacking: foo(*args, **kwargs).
if prev_type == tokenize.OP: if prev_type == tokenize.OP:
if prev_text in '}])': binary_usage = (prev_text in '}])')
need_space = True
elif prev_type == tokenize.NAME: elif prev_type == tokenize.NAME:
if prev_text not in KEYWORDS: binary_usage = (prev_text not in KEYWORDS)
else:
binary_usage = (prev_type not in SKIP_TOKENS)
if binary_usage:
if text in WS_OPTIONAL_OPERATORS:
need_space = None
else:
need_space = True need_space = True
elif prev_type not in SKIP_TOKENS: elif text in WS_OPTIONAL_OPERATORS:
need_space = True need_space = None
if need_space and start == prev_end:
if need_space is None:
# Surrounding space is optional, but ensure that
# trailing space matches opening space
need_space = (prev_end, start != prev_end)
elif need_space and start == prev_end:
# A needed opening space was not found
yield prev_end, "E225 missing whitespace around operator" yield prev_end, "E225 missing whitespace around operator"
need_space = False need_space = False
prev_type = token_type prev_type = token_type
@ -971,8 +1004,8 @@ def comparison_type(logical_line):
def python_3000_has_key(logical_line): def python_3000_has_key(logical_line):
r""" r"""
The {}.has_key() method will be removed in the future version of The {}.has_key() method is removed in the Python 3.
Python. Use the 'in' operation instead. Use the 'in' operation instead.
Okay: if "alph" in d:\n print d["alph"] Okay: if "alph" in d:\n print d["alph"]
W601: assert d.has_key('alph') W601: assert d.has_key('alph')
@ -990,21 +1023,21 @@ def python_3000_raise_comma(logical_line):
The paren-using form is preferred because when the exception arguments The paren-using form is preferred because when the exception arguments
are long or include string formatting, you don't need to use line are long or include string formatting, you don't need to use line
continuation characters thanks to the containing parentheses. The older continuation characters thanks to the containing parentheses. The older
form will be removed in Python 3000. form is removed in Python 3.
Okay: raise DummyError("Message") Okay: raise DummyError("Message")
W602: raise DummyError, "Message" W602: raise DummyError, "Message"
""" """
match = RAISE_COMMA_REGEX.match(logical_line) match = RAISE_COMMA_REGEX.match(logical_line)
if match and not RERAISE_COMMA_REGEX.match(logical_line): if match and ',' not in match.group(1):
yield match.start(1), "W602 deprecated form of raising exception" yield match.start(1) - 1, "W602 deprecated form of raising exception"
def python_3000_not_equal(logical_line): def python_3000_not_equal(logical_line):
""" """
!= can also be written <>, but this is an obsolete usage kept for != can also be written <>, but this is an obsolete usage kept for
backwards compatibility only. New code should always use !=. backwards compatibility only. New code should always use !=.
The older syntax is removed in Python 3000. The older syntax is removed in Python 3.
Okay: if a != 'no': Okay: if a != 'no':
W603: if a <> 'no': W603: if a <> 'no':
@ -1016,7 +1049,7 @@ def python_3000_not_equal(logical_line):
def python_3000_backticks(logical_line): def python_3000_backticks(logical_line):
""" """
Backticks are removed in Python 3000. Backticks are removed in Python 3.
Use repr() instead. Use repr() instead.
Okay: val = repr(1 + 2) Okay: val = repr(1 + 2)
@ -1125,7 +1158,8 @@ def parse_udiff(diff, patterns=None, parent='.'):
nrows -= 1 nrows -= 1
continue continue
if line[:3] == '@@ ': if line[:3] == '@@ ':
row, nrows = [int(g) for g in HUNK_REGEX.match(line).groups()] hunk_match = HUNK_REGEX.match(line)
row, nrows = [int(g or '1') for g in hunk_match.groups()]
rv[path].update(range(row, row + nrows)) rv[path].update(range(row, row + nrows))
elif line[:3] == '+++': elif line[:3] == '+++':
path = line[4:].split('\t', 1)[0] path = line[4:].split('\t', 1)[0]
@ -1171,7 +1205,7 @@ class Checker(object):
Load a Python source file, tokenize it, check coding style. Load a Python source file, tokenize it, check coding style.
""" """
def __init__(self, filename, lines=None, def __init__(self, filename=None, lines=None,
options=None, report=None, **kwargs): options=None, report=None, **kwargs):
if options is None: if options is None:
options = StyleGuide(kwargs).options options = StyleGuide(kwargs).options
@ -1186,9 +1220,9 @@ class Checker(object):
if filename is None: if filename is None:
self.filename = 'stdin' self.filename = 'stdin'
self.lines = lines or [] self.lines = lines or []
elif hasattr(filename, 'readlines'): elif filename == '-':
self.lines = filename.readlines()
self.filename = 'stdin' self.filename = 'stdin'
self.lines = stdin_get_value().splitlines(True)
elif lines is None: elif lines is None:
try: try:
self.lines = readlines(filename) self.lines = readlines(filename)
@ -1624,7 +1658,7 @@ class StyleGuide(object):
print('directory ' + root) print('directory ' + root)
counters['directories'] += 1 counters['directories'] += 1
for subdir in sorted(dirs): for subdir in sorted(dirs):
if self.excluded(subdir): if self.excluded(os.path.join(root, subdir)):
dirs.remove(subdir) dirs.remove(subdir)
for filename in sorted(files): for filename in sorted(files):
# contain a pattern that matches? # contain a pattern that matches?
@ -1637,7 +1671,10 @@ class StyleGuide(object):
Check if options.exclude contains a pattern that matches filename. Check if options.exclude contains a pattern that matches filename.
""" """
basename = os.path.basename(filename) basename = os.path.basename(filename)
return filename_match(basename, self.options.exclude, default=False) return any((filename_match(filename, self.options.exclude,
default=False),
filename_match(basename, self.options.exclude,
default=False)))
def ignore_code(self, code): def ignore_code(self, code):
""" """
@ -1728,11 +1765,9 @@ def selftest(options):
if match is None: if match is None:
continue continue
code, source = match.groups() code, source = match.groups()
checker = Checker(None, options=options, report=report) lines = [part.replace(r'\t', '\t') + '\n'
for part in source.split(r'\n'): for part in source.split(r'\n')]
part = part.replace(r'\t', '\t') checker = Checker(lines=lines, options=options, report=report)
part = part.replace(r'\s', ' ')
checker.lines.append(part + '\n')
checker.check_all() checker.check_all()
error = None error = None
if code == 'Okay': if code == 'Okay':
@ -1821,6 +1856,7 @@ def process_options(arglist=None, parse_argv=False, config_file=None):
version = '%s (pyflakes: %s, pep8: %s)' % \ version = '%s (pyflakes: %s, pep8: %s)' % \
(flake8_version, pyflakes_version, __version__) (flake8_version, pyflakes_version, __version__)
parser = OptionParser(version=version, parser = OptionParser(version=version,
usage="%prog [options] input ...") usage="%prog [options] input ...")
parser.config_options = [ parser.config_options = [
@ -1956,6 +1992,5 @@ def _main():
sys.stderr.write(str(report.total_errors) + '\n') sys.stderr.write(str(report.total_errors) + '\n')
sys.exit(1) sys.exit(1)
if __name__ == '__main__': if __name__ == '__main__':
_main() _main()

View file

@ -32,7 +32,7 @@ def check_file(path, ignore=(), complexity=-1):
def check_code(code, ignore=(), complexity=-1): def check_code(code, ignore=(), complexity=-1):
warnings = pyflakes.check(code, ignore, 'stdin') warnings = pyflakes.check(code, ignore, 'stdin')
warnings += pep8style.input_file(StringIO(code)) warnings += pep8style.input_file(None, lines=code.split('\n'))
if complexity > -1: if complexity > -1:
warnings += mccabe.get_code_complexity(code, complexity) warnings += mccabe.get_code_complexity(code, complexity)
return warnings return warnings