diff --git a/README.rst b/README.rst index 2326259..d52b6de 100644 --- a/README.rst +++ b/README.rst @@ -210,17 +210,17 @@ pep8: flakey: -- YW402: imported but unused -- YW403: import from line shadowed by loop variable -- YW404: 'from import ``*``' used; unable to detect undefined names -- YW405: future import(s) after other statements -- YW801: redefinition of unused from line -- YW802: undefined name -- YW803: undefined name in __all__ -- YW804: local variable (defined in enclosing scope on line ) referenced before assignment -- YW805: duplicate argument in function definition -- YW806: redefinition of function from line -- YW806: local variable is assigned to but never used +- W402: imported but unused +- W403: import from line shadowed by loop variable +- W404: 'from import ``*``' used; unable to detect undefined names +- W405: future import(s) after other statements +- W801: redefinition of unused from line +- W802: undefined name +- W803: undefined name in __all__ +- W804: local variable (defined in enclosing scope on line ) referenced before assignment +- W805: duplicate argument in function definition +- W806: redefinition of function from line +- W806: local variable is assigned to but never used McCabe: @@ -232,13 +232,11 @@ CHANGES 2.0.0 - 2013-01-xx ------------------ -- Fixes #13: pep8 and flakey are now external dependencies +- Fixes #13: pep8 and pyflakes are now external dependencies - Split run.py into main.py and hooks.py for better logic - Expose our parser for our users - New feature: Install git and hg hooks automagically -- By relying on flakey, we also fixed #45 and #35 -- Changed the way flakey errors are printed. Both the old and new versions - will be ignored when specified at the command-line though. +- By relying on pyflakes (0.6.1), we also fixed #45 and #35 1.7.0 - 2012-12-21 ------------------ diff --git a/flake8/main.py b/flake8/main.py index 0bba669..8222e9c 100644 --- a/flake8/main.py +++ b/flake8/main.py @@ -1,11 +1,12 @@ import os import sys import pep8 -import flakey +import pyflakes.api +import pyflakes.checker import select from flake8 import mccabe from flake8.util import (_initpep8, skip_file, get_parser, read_config, - merge_opts) + Flake8Reporter) pep8style = None @@ -14,55 +15,43 @@ def main(): global pep8style # parse out our flags so pep8 doesn't get confused parser = get_parser() - # This is required so we can parse out our added options - opts, args = pep8.process_options(parse_argv=True, parser=parser) + + # We then have to re-parse argv to make sure pep8 is properly initialized + pep8style = pep8.StyleGuide(parse_argv=True, config_file=True, + parser=parser) + opts = pep8style.options if opts.install_hook: from flake8.hooks import install_hook install_hook() - if opts.builtins: - s = '--builtins={0}'.format(opts.builtins) - sys.argv.remove(s) - - if opts.exit_zero: - sys.argv.remove('--exit-zero') - - if opts.install_hook: - sys.argv.remove('--install-hook') - - complexity = opts.max_complexity - if complexity > 0: - sys.argv.remove('--max-complexity={0}'.format(complexity)) - - # make sure pep8 gets the information it expects - sys.argv.pop(0) - sys.argv.insert(0, 'pep8') - read_config(opts, parser) - # We then have to re-parse argv to make sure pep8 is properly initialized - pep8style = pep8.StyleGuide(parse_argv=True, config_file=True) - merge_opts(pep8style.options, opts) warnings = 0 stdin = None + complexity = opts.max_complexity builtins = set(opts.builtins.split(',')) if builtins: - orig_builtins = set(flakey.checker._MAGIC_GLOBALS) - flakey.checker._MAGIC_GLOBALS = orig_builtins | builtins + orig_builtins = set(pyflakes.checker._MAGIC_GLOBALS) + pyflakes.checker._MAGIC_GLOBALS = orig_builtins | builtins - if pep8style.paths and pep8style.options.filename is not None: + # This is needed so we can ignore some items + pyflakes_reporter = Flake8Reporter(opts.ignore) + + if pep8style.paths and opts.filename is not None: for path in _get_python_files(pep8style.paths): if path == '-': if stdin is None: stdin = read_stdin() warnings += check_code(stdin, opts.ignore, complexity) else: - warnings += check_file(path, opts.ignore, complexity) + warnings += check_file(path, opts.ignore, complexity, + pyflakes_reporter) else: stdin = read_stdin() - warnings += check_code(stdin, opts.ignore, complexity) + warnings += check_code(stdin, opts.ignore, complexity, + pyflakes_reporter) if opts.exit_zero: raise SystemExit(0) @@ -70,27 +59,18 @@ def main(): raise SystemExit(warnings) -def _set_alt(warning): - for m in warning.messages: - m.error_code, m.alt_error_code = m.alt_error_code, m.error_code - - -def check_file(path, ignore=(), complexity=-1): +def check_file(path, ignore=(), complexity=-1, reporter=None): if pep8style.excluded(path): return 0 - warning = flakey.check_path(path) - _set_alt(warning) - warnings = flakey.print_messages(warning, ignore=ignore) + warnings = pyflakes.api.checkPath(path, reporter) warnings += pep8style.input_file(path) if complexity > -1: warnings += mccabe.get_module_complexity(path, complexity) return warnings -def check_code(code, ignore=(), complexity=-1): - warning = flakey.check(code, '') - _set_alt(warning) - warnings = flakey.print_messages(warning, ignore=ignore, code=code) +def check_code(code, ignore=(), complexity=-1, reporter=None): + warnings = pyflakes.api.check(code, '', reporter) warnings += pep8style.input_file('-', lines=code.split('\n')) if complexity > -1: warnings += mccabe.get_code_complexity(code, complexity) diff --git a/flake8/util.py b/flake8/util.py index 6228ad5..0afdd3c 100644 --- a/flake8/util.py +++ b/flake8/util.py @@ -5,6 +5,8 @@ import sys from io import StringIO import optparse import pep8 +import pyflakes +from pyflakes import reporter, messages try: # Python 2 @@ -19,7 +21,6 @@ pep8style = None def get_parser(): """Create a custom OptionParser""" from flake8 import __version__ - import flakey parser = pep8.get_parser() def version(option, opt, value, parser): @@ -27,11 +28,11 @@ def get_parser(): parser.print_version() sys.exit(0) - parser.version = '{0} (pep8: {1}, flakey: {2})'.format( - __version__, pep8.__version__, flakey.__version__) + parser.version = '{0} (pep8: {1}, pyflakes: {2})'.format( + __version__, pep8.__version__, pyflakes.__version__) parser.remove_option('--version') parser.add_option('--builtins', default='', dest='builtins', - help="append builtin functions to flakey's " + help="append builtin functions to pyflakes' " "_MAGIC_BUILTINS") parser.add_option('--exit-zero', action='store_true', default=False, help='Exit with status 0 even if there are errors') @@ -48,7 +49,9 @@ def get_parser(): def read_config(opts, opt_parser): - configs = ('.flake8', '.pep8', 'tox.ini', 'setup.cfg') + configs = ('.flake8', '.pep8', 'tox.ini', 'setup.cfg', + os.path.expanduser(r'~\.flake8'), + os.path.join(os.path.expanduser('~/.config'), 'flake8')) parser = ConfigParser() files_found = parser.read(configs) if not (files_found and parser.has_section('flake8')): @@ -86,23 +89,6 @@ def read_config(opts, opt_parser): setattr(opts, attr, val.split(',')) -def merge_opts(pep8_opts, our_opts): - pep8_parser = pep8.get_parser() - - for o in pep8_parser.option_list: - if not (o.dest and getattr(our_opts, o.dest)): - continue - - new_val = getattr(our_opts, o.dest) - old_val = getattr(pep8_opts, o.dest) - if isinstance(old_val, list): - old_val.extend(new_val) - continue - elif isinstance(old_val, tuple): - new_val = tuple(new_val) - setattr(pep8_opts, o.dest, new_val) - - def skip_warning(warning, ignore=[]): # XXX quick dirty hack, just need to keep the line in the warning if not hasattr(warning, 'message') or ignore is None: @@ -122,7 +108,13 @@ def skip_warning(warning, ignore=[]): def skip_line(line): - return line.strip().lower().endswith('# noqa') + def _noqa(line): + return line.strip().lower().endswith('# noqa') + skip = _noqa(line) + if not skip: + i = line.rfind(' #') + skip = _noqa(line[:i]) if i > 0 else False + return skip _NOQA = re.compile(r'flake8[:=]\s*noqa', re.I | re.M) @@ -161,3 +153,47 @@ def _initpep8(config_file=True): pep8style.options.max_line_length = 79 pep8style.args = [] return pep8style + + +error_mapping = { + 'W402': (messages.UnusedImport,), + 'W403': (messages.ImportShadowedByLoopVar,), + 'W404': (messages.ImportStarUsed,), + 'W405': (messages.LateFutureImport,), + 'W801': (messages.RedefinedWhileUnused, + messages.RedefinedInListComp,), + 'W802': (messages.UndefinedName,), + 'W803': (messages.UndefinedExport,), + 'W804': (messages.UndefinedLocal, + messages.UnusedVariable,), + 'W805': (messages.DuplicateArgument,), + 'W806': (messages.Redefined,), +} + + +class Flake8Reporter(reporter.Reporter): + """Our own instance of a Reporter so that we can silence some messages.""" + class_mapping = dict((k, c) for (c, v) in error_mapping.items() for k in v) + def __init__(self, ignore=None): + super(Flake8Reporter, self).__init__(sys.stdout, sys.stderr) + self.ignore = ignore or [] + + def flake(self, message): + classes = [error_mapping[i] for i in self.ignore if i in error_mapping] + + if (any(isinstance(message, c) for c in classes) or + skip_warning(message)): + return + m = self.to_str(message) + i = m.rfind(':') + 1 + message = '{0} {1}{2}'.format( + m[:i], self.class_mapping[message.__class__], m[i:] + ) + + super(Flake8Reporter, self).flake(message) + + def to_str(self, message): + try: + return unicode(message) + except NameError: + return str(message) diff --git a/setup.py b/setup.py index 8321794..29c3e1a 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( url="http://bitbucket.org/tarek/flake8", packages=["flake8", "flake8.tests"], scripts=scripts, - install_requires=["flakey (==2.0)", "pep8 (==1.4.1)"], + install_requires=["pyflakes==0.6.1", "pep8==1.4.2"], long_description=README, classifiers=[ "Environment :: Console",