diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..6294639 --- /dev/null +++ b/.hgignore @@ -0,0 +1,3 @@ +lib +include +.*\.pyc$ diff --git a/bin/flake8 b/bin/flake8 index 864c9ba..2001e00 100755 --- a/bin/flake8 +++ b/bin/flake8 @@ -1,5 +1,5 @@ #!/home/tarek/dev/bitbucket.org/flake8-clean/bin/python -from flake8 import main +from flake8.run import main if __name__ == '__main__': main() diff --git a/flake8/__init__.py b/flake8/__init__.py index e9407cb..792d600 100644 --- a/flake8/__init__.py +++ b/flake8/__init__.py @@ -1,176 +1 @@ - -""" -Implementation of the command-line I{flake8} tool. -""" -import sys -import os -import _ast -import pep8 -import mccabe -import re - - -checker = __import__('flake8.checker').checker - - -def check(codeString, filename): - """ - Check the Python source given by C{codeString} for flakes. - - @param codeString: The Python source to check. - @type codeString: C{str} - - @param filename: The name of the file the source came from, used to report - errors. - @type filename: C{str} - - @return: The number of warnings emitted. - @rtype: C{int} - """ - # First, compile into an AST and handle syntax errors. - try: - tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST) - except SyntaxError, value: - msg = value.args[0] - - (lineno, offset, text) = value.lineno, value.offset, value.text - - # If there's an encoding problem with the file, the text is None. - if text is None: - # Avoid using msg, since for the only known case, it contains a - # bogus message that claims the encoding the file declared was - # unknown. - print >> sys.stderr, "%s: problem decoding source" % (filename, ) - else: - line = text.splitlines()[-1] - - if offset is not None: - offset = offset - (len(text) - len(line)) - - print >> sys.stderr, '%s:%d: %s' % (filename, lineno, msg) - print >> sys.stderr, line - - if offset is not None: - print >> sys.stderr, " " * offset, "^" - - return 1 - else: - # Okay, it's syntactically valid. Now check it. - w = checker.Checker(tree, filename) - w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) - valid_warnings = 0 - - for warning in w.messages: - if _noqa(warning): - continue - print warning - valid_warnings += 1 - - return valid_warnings - - -def _noqa(warning): - # XXX quick dirty hack, just need to keep the line in the warning - line = open(warning.filename).readlines()[warning.lineno-1] - return line.strip().lower().endswith('# noqa') - - -def checkPath(filename): - """ - Check the given path, printing out any warnings detected. - - @return: the number of warnings printed - """ - try: - return check(file(filename, 'U').read() + '\n', filename) - except IOError, msg: - print >> sys.stderr, "%s: %s" % (filename, msg.args[1]) - return 1 - - -def check_file(path, complexity=10): - warnings = checkPath(path) - warnings += pep8.input_file(path) - warnings += mccabe.get_module_complexity(path, complexity) - return warnings - - -def check_code(code, complexity=10): - warnings = check(code, '') - warnings += mccabe.get_code_complexity(code, complexity) - return warnings - - -_NOQA = re.compile(r'^# flake8: noqa', re.I | re.M) - - -def skip_file(path): - """Returns True if this header is found in path - - # flake8: noqa - """ - f = open(path) - try: - content = f.read() - finally: - f.close() - return _NOQA.match(content) is not None - - -def _get_python_files(paths): - for path in paths: - if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - if not filename.endswith('.py'): - continue - fullpath = os.path.join(dirpath, filename) - if not skip_file(fullpath): - yield fullpath - - else: - if not skip_file(path): - yield path - - -def main(): - pep8.process_options() - warnings = 0 - args = sys.argv[1:] - if args: - for path in _get_python_files(args): - warnings += check_file(path) - else: - stdin = sys.stdin.read() - warnings += check_code(stdin) - - raise SystemExit(warnings > 0) - - -def _get_files(repo, **kwargs): - for rev in xrange(repo[kwargs['node']], len(repo)): - for file_ in repo[rev].files(): - if not file_.endswith('.py'): - continue - if skip_file(file_): - continue - yield file_ - - -def hg_hook(ui, repo, **kwargs): - pep8.process_options() - warnings = 0 - for file_ in _get_files(repo, **kwargs): - warnings += check_file(file_) - - strict = ui.config('flake8', 'strict') - if strict is None: - strict = True - - if strict.lower() in ('1', 'true'): - return warnings - - return 0 - -if __name__ == '__main__': - main() +# diff --git a/flake8/pep8.py b/flake8/pep8.py index fad2429..ec4576a 100644 --- a/flake8/pep8.py +++ b/flake8/pep8.py @@ -105,6 +105,8 @@ from optparse import OptionParser from keyword import iskeyword from fnmatch import fnmatch +from flake8.util import skip_line + DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' DEFAULT_IGNORE = ['E24'] @@ -936,7 +938,7 @@ class Checker(object): """ Report an error, according to options. """ - if self.physical_line.strip().lower().endswith('# noqa'): + if skip_line(self.physical_line): return if options.quiet == 1 and not self.file_errors: message(self.filename) diff --git a/flake8/checker.py b/flake8/pyflakes.py similarity index 91% rename from flake8/checker.py rename to flake8/pyflakes.py index c0c6077..a201a06 100644 --- a/flake8/checker.py +++ b/flake8/pyflakes.py @@ -5,8 +5,10 @@ import __builtin__ import os.path import _ast +import sys from flake8 import messages +from flake8.util import skip_warning # utility function to iterate over an AST node's children, adapted # from Python 2.6's standard ast module @@ -616,3 +618,71 @@ class Checker(object): if node.module == '__future__': importation.used = (self.scope, node.lineno) self.addBinding(node.lineno, importation) + +def checkPath(filename): + """ + Check the given path, printing out any warnings detected. + + @return: the number of warnings printed + """ + try: + return check(file(filename, 'U').read() + '\n', filename) + except IOError, msg: + print >> sys.stderr, "%s: %s" % (filename, msg.args[1]) + return 1 + +def check(codeString, filename): + """ + Check the Python source given by C{codeString} for flakes. + + @param codeString: The Python source to check. + @type codeString: C{str} + + @param filename: The name of the file the source came from, used to report + errors. + @type filename: C{str} + + @return: The number of warnings emitted. + @rtype: C{int} + """ + # First, compile into an AST and handle syntax errors. + try: + tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST) + except SyntaxError, value: + msg = value.args[0] + + (lineno, offset, text) = value.lineno, value.offset, value.text + + # If there's an encoding problem with the file, the text is None. + if text is None: + # Avoid using msg, since for the only known case, it contains a + # bogus message that claims the encoding the file declared was + # unknown. + print >> sys.stderr, "%s: problem decoding source" % (filename, ) + else: + line = text.splitlines()[-1] + + if offset is not None: + offset = offset - (len(text) - len(line)) + + print >> sys.stderr, '%s:%d: %s' % (filename, lineno, msg) + print >> sys.stderr, line + + if offset is not None: + print >> sys.stderr, " " * offset, "^" + + return 1 + else: + # Okay, it's syntactically valid. Now check it. + w = Checker(tree, filename) + w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) + valid_warnings = 0 + + for warning in w.messages: + if skip_warning(warning): + continue + print warning + valid_warnings += 1 + + return valid_warnings + diff --git a/flake8/run.py b/flake8/run.py new file mode 100644 index 0000000..ce7728f --- /dev/null +++ b/flake8/run.py @@ -0,0 +1,84 @@ + +""" +Implementation of the command-line I{flake8} tool. +""" +import sys +import os + +from flake8.util import skip_file +from flake8 import pep8 +from flake8 import pyflakes +from flake8 import mccabe + + +def check_file(path, complexity=10): + + warnings = pyflakes.checkPath(path) + warnings += pep8.input_file(path) + warnings += mccabe.get_module_complexity(path, complexity) + return warnings + + +def check_code(code, complexity=10): + warnings = pyflakes.check(code, '') + warnings += mccabe.get_code_complexity(code, complexity) + return warnings + + +def _get_python_files(paths): + for path in paths: + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk(path): + for filename in filenames: + if not filename.endswith('.py'): + continue + fullpath = os.path.join(dirpath, filename) + if not skip_file(fullpath): + yield fullpath + + else: + if not skip_file(path): + yield path + + +def main(): + pep8.process_options() + warnings = 0 + args = sys.argv[1:] + if args: + for path in _get_python_files(args): + warnings += check_file(path) + else: + stdin = sys.stdin.read() + warnings += check_code(stdin) + + raise SystemExit(warnings > 0) + + +def _get_files(repo, **kwargs): + for rev in xrange(repo[kwargs['node']], len(repo)): + for file_ in repo[rev].files(): + if not file_.endswith('.py'): + continue + if skip_file(file_): + continue + yield file_ + + +def hg_hook(ui, repo, **kwargs): + pep8.process_options() + warnings = 0 + for file_ in _get_files(repo, **kwargs): + warnings += check_file(file_) + + strict = ui.config('flake8', 'strict') + if strict is None: + strict = True + + if strict.lower() in ('1', 'true'): + return warnings + + return 0 + +if __name__ == '__main__': + main() diff --git a/flake8/util.py b/flake8/util.py new file mode 100644 index 0000000..87a340e --- /dev/null +++ b/flake8/util.py @@ -0,0 +1,26 @@ +import re + + +def skip_warning(warning): + # XXX quick dirty hack, just need to keep the line in the warning + line = open(warning.filename).readlines()[warning.lineno-1] + return skip_line(line) + +def skip_line(line): + return line.strip().lower().endswith('# noqa') + + +_NOQA = re.compile(r'^# flake8: noqa', re.I | re.M) + + +def skip_file(path): + """Returns True if this header is found in path + + # flake8: noqa + """ + f = open(path) + try: + content = f.read() + finally: + f.close() + return _NOQA.match(content) is not None