Commits

Tarek Ziadé  committed 45642b2

code cleanup

  • Participants
  • Parent commits ed98227

Comments (0)

Files changed (8)

+lib
+include
+.*\.pyc$
 #!/home/tarek/dev/bitbucket.org/flake8-clean/bin/python
-from flake8 import main
+from flake8.run import main
 
 if __name__ == '__main__':
     main()

File flake8/__init__.py

-
-"""
-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, '<stdin>')
-    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()
+#

File flake8/checker.py

-# -*- test-case-name: pyflakes -*-
-# (c) 2005-2010 Divmod, Inc.
-# See LICENSE file for details
-
-import __builtin__
-import os.path
-import _ast
-
-from flake8 import messages
-
-# utility function to iterate over an AST node's children, adapted
-# from Python 2.6's standard ast module
-try:
-    import ast
-    iter_child_nodes = ast.iter_child_nodes
-except (ImportError, AttributeError):
-
-    def iter_child_nodes(node, astcls=_ast.AST):
-        """
-        Yield all direct child nodes of *node*, that is, all fields that are
-        nodes and all items of fields that are lists of nodes.
-        """
-        for name in node._fields:
-            field = getattr(node, name, None)
-            if isinstance(field, astcls):
-                yield field
-            elif isinstance(field, list):
-                for item in field:
-                    yield item
-
-
-class Binding(object):
-    """
-    Represents the binding of a value to a name.
-
-    The checker uses this to keep track of which names have been bound and
-    which names have not. See L{Assignment} for a special type of binding that
-    is checked with stricter rules.
-
-    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
-                line number that this binding was last used
-    """
-
-    def __init__(self, name, source):
-        self.name = name
-        self.source = source
-        self.used = False
-
-    def __str__(self):
-        return self.name
-
-    def __repr__(self):
-        return '<%s object %r from line %r at 0x%x>' % (
-            self.__class__.__name__,
-            self.name,
-            self.source.lineno,
-            id(self))
-
-
-class UnBinding(Binding):
-    '''Created by the 'del' operator.'''
-
-
-class Importation(Binding):
-    """
-    A binding created by an import statement.
-
-    @ivar fullName: The complete name given to the import statement,
-        possibly including multiple dotted components.
-    @type fullName: C{str}
-    """
-    def __init__(self, name, source):
-        self.fullName = name
-        name = name.split('.')[0]
-        super(Importation, self).__init__(name, source)
-
-
-class Argument(Binding):
-    """
-    Represents binding a name as an argument.
-    """
-
-
-class Assignment(Binding):
-    """
-    Represents binding a name with an explicit assignment.
-
-    The checker will raise warnings for any Assignment that isn't used. Also,
-    the checker does not consider assignments in tuple/list unpacking to be
-    Assignments, rather it treats them as simple Bindings.
-    """
-
-
-class FunctionDefinition(Binding):
-    pass
-
-
-class ExportBinding(Binding):
-    """
-    A binding created by an C{__all__} assignment.  If the names in the list
-    can be determined statically, they will be treated as names for export and
-    additional checking applied to them.
-
-    The only C{__all__} assignment that can be recognized is one which takes
-    the value of a literal list containing literal strings.  For example::
-
-        __all__ = ["foo", "bar"]
-
-    Names which are imported and not otherwise used but appear in the value of
-    C{__all__} will not have an unused import warning reported for them.
-    """
-    def names(self):
-        """
-        Return a list of the names referenced by this binding.
-        """
-        names = []
-        if isinstance(self.source, _ast.List):
-            for node in self.source.elts:
-                if isinstance(node, _ast.Str):
-                    names.append(node.s)
-        return names
-
-
-class Scope(dict):
-    importStarred = False       # set to True when import * is found
-
-    def __repr__(self):
-        return '<%s at 0x%x %s>' % (self.__class__.__name__,
-                                    id(self),
-                                    dict.__repr__(self))
-
-    def __init__(self):
-        super(Scope, self).__init__()
-
-
-class ClassScope(Scope):
-    pass
-
-
-class FunctionScope(Scope):
-    """
-    I represent a name scope for a function.
-
-    @ivar globals: Names declared 'global' in this function.
-    """
-    def __init__(self):
-        super(FunctionScope, self).__init__()
-        self.globals = {}
-
-
-class ModuleScope(Scope):
-    pass
-
-
-# Globally defined names which are not attributes of the __builtin__ module.
-_MAGIC_GLOBALS = ['__file__', '__builtins__']
-
-
-class Checker(object):
-    """
-    I check the cleanliness and sanity of Python code.
-
-    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
-        of the list are two-tuples.  The first element is the callable passed
-        to L{deferFunction}.  The second element is a copy of the scope stack
-        at the time L{deferFunction} was called.
-
-    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
-        callables which are deferred assignment checks.
-    """
-
-    nodeDepth = 0
-    traceTree = False
-
-    def __init__(self, tree, filename='(none)'):
-        self._deferredFunctions = []
-        self._deferredAssignments = []
-        self.dead_scopes = []
-        self.messages = []
-        self.filename = filename
-        self.scopeStack = [ModuleScope()]
-        self.futuresAllowed = True
-        self.handleChildren(tree)
-        self._runDeferred(self._deferredFunctions)
-        # Set _deferredFunctions to None so that deferFunction will fail
-        # noisily if called after we've run through the deferred functions.
-        self._deferredFunctions = None
-        self._runDeferred(self._deferredAssignments)
-        # Set _deferredAssignments to None so that deferAssignment will fail
-        # noisly if called after we've run through the deferred assignments.
-        self._deferredAssignments = None
-        del self.scopeStack[1:]
-        self.popScope()
-        self.check_dead_scopes()
-
-    def deferFunction(self, callable):
-        '''
-        Schedule a function handler to be called just before completion.
-
-        This is used for handling function bodies, which must be deferred
-        because code later in the file might modify the global scope. When
-        `callable` is called, the scope at the time this is called will be
-        restored, however it will contain any new bindings added to it.
-        '''
-        self._deferredFunctions.append((callable, self.scopeStack[:]))
-
-    def deferAssignment(self, callable):
-        """
-        Schedule an assignment handler to be called just after deferred
-        function handlers.
-        """
-        self._deferredAssignments.append((callable, self.scopeStack[:]))
-
-    def _runDeferred(self, deferred):
-        """
-        Run the callables in C{deferred} using their associated scope stack.
-        """
-        for handler, scope in deferred:
-            self.scopeStack = scope
-            handler()
-
-    def scope(self):
-        return self.scopeStack[-1]
-    scope = property(scope)
-
-    def popScope(self):
-        self.dead_scopes.append(self.scopeStack.pop())
-
-    def check_dead_scopes(self):
-        """
-        Look at scopes which have been fully examined and report names in them
-        which were imported but unused.
-        """
-        for scope in self.dead_scopes:
-            export = isinstance(scope.get('__all__'), ExportBinding)
-            if export:
-                all = scope['__all__'].names()
-                if os.path.split(self.filename)[1] != '__init__.py':
-                    # Look for possible mistakes in the export list
-                    undefined = set(all) - set(scope)
-                    for name in undefined:
-                        self.report(
-                            messages.UndefinedExport,
-                            scope['__all__'].source.lineno,
-                            name)
-            else:
-                all = []
-
-            # Look for imported names that aren't used.
-            for importation in scope.itervalues():
-                if isinstance(importation, Importation):
-                    if not importation.used and importation.name not in all:
-                        self.report(
-                            messages.UnusedImport,
-                            importation.source.lineno,
-                            importation.name)
-
-    def pushFunctionScope(self):
-        self.scopeStack.append(FunctionScope())
-
-    def pushClassScope(self):
-        self.scopeStack.append(ClassScope())
-
-    def report(self, messageClass, *args, **kwargs):
-        self.messages.append(messageClass(self.filename, *args, **kwargs))
-
-    def handleChildren(self, tree):
-        for node in iter_child_nodes(tree):
-            self.handleNode(node, tree)
-
-    def isDocstring(self, node):
-        """
-        Determine if the given node is a docstring, as long as it is at the
-        correct place in the node tree.
-        """
-        return isinstance(node, _ast.Str) or \
-               (isinstance(node, _ast.Expr) and
-                isinstance(node.value, _ast.Str))
-
-    def handleNode(self, node, parent):
-        node.parent = parent
-        if self.traceTree:
-            print '  ' * self.nodeDepth + node.__class__.__name__
-        self.nodeDepth += 1
-        if self.futuresAllowed and not \
-               (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)):
-            self.futuresAllowed = False
-        nodeType = node.__class__.__name__.upper()
-        try:
-            handler = getattr(self, nodeType)
-            handler(node)
-        finally:
-            self.nodeDepth -= 1
-        if self.traceTree:
-            print '  ' * self.nodeDepth + 'end ' + node.__class__.__name__
-
-    def ignore(self, node):
-        pass
-
-    # "stmt" type nodes
-    RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
-        TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
-
-    CONTINUE = BREAK = PASS = ignore
-
-    # "expr" type nodes
-    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
-    CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
-
-    NUM = STR = ELLIPSIS = ignore
-
-    # "slice" type nodes
-    SLICE = EXTSLICE = INDEX = handleChildren
-
-    # expression contexts are node instances too, though being constants
-    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
-
-    # same for operators
-    AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
-    BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
-    EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
-
-    # additional node types
-    COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
-
-    def addBinding(self, lineno, value, reportRedef=True):
-        '''Called when a binding is altered.
-
-        - `lineno` is the line of the statement responsible for the change
-        - `value` is the optional new value, a Binding instance, associated
-          with the binding; if None, the binding is deleted if it exists.
-        - if `reportRedef` is True (default), rebinding while unused will be
-          reported.
-        '''
-        if (isinstance(self.scope.get(value.name), FunctionDefinition)
-                    and isinstance(value, FunctionDefinition)):
-            self.report(messages.RedefinedFunction,
-                        lineno, value.name,
-                        self.scope[value.name].source.lineno)
-
-        if not isinstance(self.scope, ClassScope):
-            for scope in self.scopeStack[::-1]:
-                existing = scope.get(value.name)
-                if (isinstance(existing, Importation)
-                        and not existing.used
-                        and (not isinstance(value, Importation) \
-                             or value.fullName == existing.fullName)
-                        and reportRedef):
-
-                    self.report(messages.RedefinedWhileUnused,
-                                lineno, value.name,
-                                scope[value.name].source.lineno)
-
-        if isinstance(value, UnBinding):
-            try:
-                del self.scope[value.name]
-            except KeyError:
-                self.report(messages.UndefinedName, lineno, value.name)
-        else:
-            self.scope[value.name] = value
-
-    def GLOBAL(self, node):
-        """
-        Keep track of globals declarations.
-        """
-        if isinstance(self.scope, FunctionScope):
-            self.scope.globals.update(dict.fromkeys(node.names))
-
-    def LISTCOMP(self, node):
-        # handle generators before element
-        for gen in node.generators:
-            self.handleNode(gen, node)
-        self.handleNode(node.elt, node)
-
-    GENERATOREXP = SETCOMP = LISTCOMP
-
-    # dictionary comprehensions; introduced in Python 2.7
-    def DICTCOMP(self, node):
-        for gen in node.generators:
-            self.handleNode(gen, node)
-        self.handleNode(node.key, node)
-        self.handleNode(node.value, node)
-
-    def FOR(self, node):
-        """
-        Process bindings for loop variables.
-        """
-        vars = []
-
-        def collectLoopVars(n):
-            if isinstance(n, _ast.Name):
-                vars.append(n.id)
-            elif isinstance(n, _ast.expr_context):
-                return
-            else:
-                for c in iter_child_nodes(n):
-                    collectLoopVars(c)
-
-        collectLoopVars(node.target)
-        for varn in vars:
-            if (isinstance(self.scope.get(varn), Importation)
-                    # unused ones will get an unused import warning
-                    and self.scope[varn].used):
-                self.report(messages.ImportShadowedByLoopVar,
-                            node.lineno, varn, self.scope[varn].source.lineno)
-
-        self.handleChildren(node)
-
-    def NAME(self, node):
-        """
-        Handle occurrence of Name (which can be a load/store/delete access.)
-        """
-        # Locate the name in locals / function / globals scopes.
-        if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)):
-            # try local scope
-            importStarred = self.scope.importStarred
-            try:
-                self.scope[node.id].used = (self.scope, node.lineno)
-            except KeyError:
-                pass
-            else:
-                return
-
-            # try enclosing function scopes
-
-            for scope in self.scopeStack[-2:0:-1]:
-                importStarred = importStarred or scope.importStarred
-                if not isinstance(scope, FunctionScope):
-                    continue
-                try:
-                    scope[node.id].used = (self.scope, node.lineno)
-                except KeyError:
-                    pass
-                else:
-                    return
-
-            # try global scope
-
-            importStarred = importStarred or self.scopeStack[0].importStarred
-            try:
-                self.scopeStack[0][node.id].used = (self.scope, node.lineno)
-            except KeyError:
-                if ((not hasattr(__builtin__, node.id))
-                        and node.id not in _MAGIC_GLOBALS
-                        and not importStarred):
-                    if (os.path.basename(self.filename) == '__init__.py' and
-                        node.id == '__path__'):
-                        # the special name __path__ is valid only in packages
-                        pass
-                    else:
-                        self.report(messages.UndefinedName,
-                                    node.lineno,
-                                    node.id)
-
-        elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)):
-            # if the name hasn't already been defined in the current scope
-            if isinstance(self.scope, FunctionScope) and \
-               node.id not in self.scope:
-                # for each function or module scope above us
-                for scope in self.scopeStack[:-1]:
-                    if not isinstance(scope, (FunctionScope, ModuleScope)):
-                        continue
-                    # if the name was defined in that scope, and the name has
-                    # been accessed already in the current scope, and hasn't
-                    # been declared global
-                    if (node.id in scope
-                            and scope[node.id].used
-                            and scope[node.id].used[0] is self.scope
-                            and node.id not in self.scope.globals):
-                        # then it's probably a mistake
-                        self.report(messages.UndefinedLocal,
-                                    scope[node.id].used[1],
-                                    node.id,
-                                    scope[node.id].source.lineno)
-                        break
-
-            if isinstance(node.parent,
-                          (_ast.For,
-                           _ast.comprehension,
-                           _ast.Tuple,
-                           _ast.List)):
-                binding = Binding(node.id, node)
-            elif (node.id == '__all__' and
-                  isinstance(self.scope, ModuleScope)):
-                binding = ExportBinding(node.id, node.parent.value)
-            else:
-                binding = Assignment(node.id, node)
-            if node.id in self.scope:
-                binding.used = self.scope[node.id].used
-            self.addBinding(node.lineno, binding)
-        elif isinstance(node.ctx, _ast.Del):
-            if isinstance(self.scope, FunctionScope) and \
-                   node.id in self.scope.globals:
-                del self.scope.globals[node.id]
-            else:
-                self.addBinding(node.lineno, UnBinding(node.id, node))
-        else:
-            # must be a Param context -- this only happens for names
-            # in function arguments, but these aren't dispatched through here
-            raise RuntimeError(
-                "Got impossible expression context: %r" % (node.ctx,))
-
-    def FUNCTIONDEF(self, node):
-        # the decorators attribute is called decorator_list as of Python 2.6
-        if hasattr(node, 'decorators'):
-            for deco in node.decorators:
-                self.handleNode(deco, node)
-        else:
-            for deco in node.decorator_list:
-                self.handleNode(deco, node)
-        self.addBinding(node.lineno, FunctionDefinition(node.name, node))
-        self.LAMBDA(node)
-
-    def LAMBDA(self, node):
-        for default in node.args.defaults:
-            self.handleNode(default, node)
-
-        def runFunction():
-            args = []
-
-            def addArgs(arglist):
-                for arg in arglist:
-                    if isinstance(arg, _ast.Tuple):
-                        addArgs(arg.elts)
-                    else:
-                        if arg.id in args:
-                            self.report(messages.DuplicateArgument,
-                                        node.lineno, arg.id)
-                        args.append(arg.id)
-
-            self.pushFunctionScope()
-            addArgs(node.args.args)
-            # vararg/kwarg identifiers are not Name nodes
-            if node.args.vararg:
-                args.append(node.args.vararg)
-            if node.args.kwarg:
-                args.append(node.args.kwarg)
-            for name in args:
-                self.addBinding(node.lineno, Argument(name, node),
-                                reportRedef=False)
-            if isinstance(node.body, list):
-                # case for FunctionDefs
-                for stmt in node.body:
-                    self.handleNode(stmt, node)
-            else:
-                # case for Lambdas
-                self.handleNode(node.body, node)
-
-            def checkUnusedAssignments():
-                """
-                Check to see if any assignments have not been used.
-                """
-                for name, binding in self.scope.iteritems():
-                    if (not binding.used and not name in self.scope.globals
-                        and isinstance(binding, Assignment)):
-                        self.report(messages.UnusedVariable,
-                                    binding.source.lineno, name)
-            self.deferAssignment(checkUnusedAssignments)
-            self.popScope()
-
-        self.deferFunction(runFunction)
-
-    def CLASSDEF(self, node):
-        """
-        Check names used in a class definition, including its decorators, base
-        classes, and the body of its definition.  Additionally, add its name to
-        the current scope.
-        """
-        # decorator_list is present as of Python 2.6
-        for deco in getattr(node, 'decorator_list', []):
-            self.handleNode(deco, node)
-        for baseNode in node.bases:
-            self.handleNode(baseNode, node)
-        self.pushClassScope()
-        for stmt in node.body:
-            self.handleNode(stmt, node)
-        self.popScope()
-        self.addBinding(node.lineno, Binding(node.name, node))
-
-    def ASSIGN(self, node):
-        self.handleNode(node.value, node)
-        for target in node.targets:
-            self.handleNode(target, node)
-
-    def AUGASSIGN(self, node):
-        # AugAssign is awkward: must set the context explicitly
-        # and visit twice, once with AugLoad context, once with
-        # AugStore context
-        node.target.ctx = _ast.AugLoad()
-        self.handleNode(node.target, node)
-        self.handleNode(node.value, node)
-        node.target.ctx = _ast.AugStore()
-        self.handleNode(node.target, node)
-
-    def IMPORT(self, node):
-        for alias in node.names:
-            name = alias.asname or alias.name
-            importation = Importation(name, node)
-            self.addBinding(node.lineno, importation)
-
-    def IMPORTFROM(self, node):
-        if node.module == '__future__':
-            if not self.futuresAllowed:
-                self.report(messages.LateFutureImport, node.lineno,
-                            [n.name for n in node.names])
-        else:
-            self.futuresAllowed = False
-
-        for alias in node.names:
-            if alias.name == '*':
-                self.scope.importStarred = True
-                self.report(messages.ImportStarUsed, node.lineno, node.module)
-                continue
-            name = alias.asname or alias.name
-            importation = Importation(name, node)
-            if node.module == '__future__':
-                importation.used = (self.scope, node.lineno)
-            self.addBinding(node.lineno, importation)

File flake8/pep8.py

 from keyword import iskeyword
 from fnmatch import fnmatch
 
+from flake8.util import skip_line
+
 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git'
 DEFAULT_IGNORE = ['E24']
 
         """
         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)

File flake8/pyflakes.py

+# -*- test-case-name: pyflakes -*-
+# (c) 2005-2010 Divmod, Inc.
+# See LICENSE file for details
+
+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
+try:
+    import ast
+    iter_child_nodes = ast.iter_child_nodes
+except (ImportError, AttributeError):
+
+    def iter_child_nodes(node, astcls=_ast.AST):
+        """
+        Yield all direct child nodes of *node*, that is, all fields that are
+        nodes and all items of fields that are lists of nodes.
+        """
+        for name in node._fields:
+            field = getattr(node, name, None)
+            if isinstance(field, astcls):
+                yield field
+            elif isinstance(field, list):
+                for item in field:
+                    yield item
+
+
+class Binding(object):
+    """
+    Represents the binding of a value to a name.
+
+    The checker uses this to keep track of which names have been bound and
+    which names have not. See L{Assignment} for a special type of binding that
+    is checked with stricter rules.
+
+    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
+                line number that this binding was last used
+    """
+
+    def __init__(self, name, source):
+        self.name = name
+        self.source = source
+        self.used = False
+
+    def __str__(self):
+        return self.name
+
+    def __repr__(self):
+        return '<%s object %r from line %r at 0x%x>' % (
+            self.__class__.__name__,
+            self.name,
+            self.source.lineno,
+            id(self))
+
+
+class UnBinding(Binding):
+    '''Created by the 'del' operator.'''
+
+
+class Importation(Binding):
+    """
+    A binding created by an import statement.
+
+    @ivar fullName: The complete name given to the import statement,
+        possibly including multiple dotted components.
+    @type fullName: C{str}
+    """
+    def __init__(self, name, source):
+        self.fullName = name
+        name = name.split('.')[0]
+        super(Importation, self).__init__(name, source)
+
+
+class Argument(Binding):
+    """
+    Represents binding a name as an argument.
+    """
+
+
+class Assignment(Binding):
+    """
+    Represents binding a name with an explicit assignment.
+
+    The checker will raise warnings for any Assignment that isn't used. Also,
+    the checker does not consider assignments in tuple/list unpacking to be
+    Assignments, rather it treats them as simple Bindings.
+    """
+
+
+class FunctionDefinition(Binding):
+    pass
+
+
+class ExportBinding(Binding):
+    """
+    A binding created by an C{__all__} assignment.  If the names in the list
+    can be determined statically, they will be treated as names for export and
+    additional checking applied to them.
+
+    The only C{__all__} assignment that can be recognized is one which takes
+    the value of a literal list containing literal strings.  For example::
+
+        __all__ = ["foo", "bar"]
+
+    Names which are imported and not otherwise used but appear in the value of
+    C{__all__} will not have an unused import warning reported for them.
+    """
+    def names(self):
+        """
+        Return a list of the names referenced by this binding.
+        """
+        names = []
+        if isinstance(self.source, _ast.List):
+            for node in self.source.elts:
+                if isinstance(node, _ast.Str):
+                    names.append(node.s)
+        return names
+
+
+class Scope(dict):
+    importStarred = False       # set to True when import * is found
+
+    def __repr__(self):
+        return '<%s at 0x%x %s>' % (self.__class__.__name__,
+                                    id(self),
+                                    dict.__repr__(self))
+
+    def __init__(self):
+        super(Scope, self).__init__()
+
+
+class ClassScope(Scope):
+    pass
+
+
+class FunctionScope(Scope):
+    """
+    I represent a name scope for a function.
+
+    @ivar globals: Names declared 'global' in this function.
+    """
+    def __init__(self):
+        super(FunctionScope, self).__init__()
+        self.globals = {}
+
+
+class ModuleScope(Scope):
+    pass
+
+
+# Globally defined names which are not attributes of the __builtin__ module.
+_MAGIC_GLOBALS = ['__file__', '__builtins__']
+
+
+class Checker(object):
+    """
+    I check the cleanliness and sanity of Python code.
+
+    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
+        of the list are two-tuples.  The first element is the callable passed
+        to L{deferFunction}.  The second element is a copy of the scope stack
+        at the time L{deferFunction} was called.
+
+    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
+        callables which are deferred assignment checks.
+    """
+
+    nodeDepth = 0
+    traceTree = False
+
+    def __init__(self, tree, filename='(none)'):
+        self._deferredFunctions = []
+        self._deferredAssignments = []
+        self.dead_scopes = []
+        self.messages = []
+        self.filename = filename
+        self.scopeStack = [ModuleScope()]
+        self.futuresAllowed = True
+        self.handleChildren(tree)
+        self._runDeferred(self._deferredFunctions)
+        # Set _deferredFunctions to None so that deferFunction will fail
+        # noisily if called after we've run through the deferred functions.
+        self._deferredFunctions = None
+        self._runDeferred(self._deferredAssignments)
+        # Set _deferredAssignments to None so that deferAssignment will fail
+        # noisly if called after we've run through the deferred assignments.
+        self._deferredAssignments = None
+        del self.scopeStack[1:]
+        self.popScope()
+        self.check_dead_scopes()
+
+    def deferFunction(self, callable):
+        '''
+        Schedule a function handler to be called just before completion.
+
+        This is used for handling function bodies, which must be deferred
+        because code later in the file might modify the global scope. When
+        `callable` is called, the scope at the time this is called will be
+        restored, however it will contain any new bindings added to it.
+        '''
+        self._deferredFunctions.append((callable, self.scopeStack[:]))
+
+    def deferAssignment(self, callable):
+        """
+        Schedule an assignment handler to be called just after deferred
+        function handlers.
+        """
+        self._deferredAssignments.append((callable, self.scopeStack[:]))
+
+    def _runDeferred(self, deferred):
+        """
+        Run the callables in C{deferred} using their associated scope stack.
+        """
+        for handler, scope in deferred:
+            self.scopeStack = scope
+            handler()
+
+    def scope(self):
+        return self.scopeStack[-1]
+    scope = property(scope)
+
+    def popScope(self):
+        self.dead_scopes.append(self.scopeStack.pop())
+
+    def check_dead_scopes(self):
+        """
+        Look at scopes which have been fully examined and report names in them
+        which were imported but unused.
+        """
+        for scope in self.dead_scopes:
+            export = isinstance(scope.get('__all__'), ExportBinding)
+            if export:
+                all = scope['__all__'].names()
+                if os.path.split(self.filename)[1] != '__init__.py':
+                    # Look for possible mistakes in the export list
+                    undefined = set(all) - set(scope)
+                    for name in undefined:
+                        self.report(
+                            messages.UndefinedExport,
+                            scope['__all__'].source.lineno,
+                            name)
+            else:
+                all = []
+
+            # Look for imported names that aren't used.
+            for importation in scope.itervalues():
+                if isinstance(importation, Importation):
+                    if not importation.used and importation.name not in all:
+                        self.report(
+                            messages.UnusedImport,
+                            importation.source.lineno,
+                            importation.name)
+
+    def pushFunctionScope(self):
+        self.scopeStack.append(FunctionScope())
+
+    def pushClassScope(self):
+        self.scopeStack.append(ClassScope())
+
+    def report(self, messageClass, *args, **kwargs):
+        self.messages.append(messageClass(self.filename, *args, **kwargs))
+
+    def handleChildren(self, tree):
+        for node in iter_child_nodes(tree):
+            self.handleNode(node, tree)
+
+    def isDocstring(self, node):
+        """
+        Determine if the given node is a docstring, as long as it is at the
+        correct place in the node tree.
+        """
+        return isinstance(node, _ast.Str) or \
+               (isinstance(node, _ast.Expr) and
+                isinstance(node.value, _ast.Str))
+
+    def handleNode(self, node, parent):
+        node.parent = parent
+        if self.traceTree:
+            print '  ' * self.nodeDepth + node.__class__.__name__
+        self.nodeDepth += 1
+        if self.futuresAllowed and not \
+               (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)):
+            self.futuresAllowed = False
+        nodeType = node.__class__.__name__.upper()
+        try:
+            handler = getattr(self, nodeType)
+            handler(node)
+        finally:
+            self.nodeDepth -= 1
+        if self.traceTree:
+            print '  ' * self.nodeDepth + 'end ' + node.__class__.__name__
+
+    def ignore(self, node):
+        pass
+
+    # "stmt" type nodes
+    RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
+        TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
+
+    CONTINUE = BREAK = PASS = ignore
+
+    # "expr" type nodes
+    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
+    CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
+
+    NUM = STR = ELLIPSIS = ignore
+
+    # "slice" type nodes
+    SLICE = EXTSLICE = INDEX = handleChildren
+
+    # expression contexts are node instances too, though being constants
+    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
+
+    # same for operators
+    AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
+    BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
+    EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
+
+    # additional node types
+    COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
+
+    def addBinding(self, lineno, value, reportRedef=True):
+        '''Called when a binding is altered.
+
+        - `lineno` is the line of the statement responsible for the change
+        - `value` is the optional new value, a Binding instance, associated
+          with the binding; if None, the binding is deleted if it exists.
+        - if `reportRedef` is True (default), rebinding while unused will be
+          reported.
+        '''
+        if (isinstance(self.scope.get(value.name), FunctionDefinition)
+                    and isinstance(value, FunctionDefinition)):
+            self.report(messages.RedefinedFunction,
+                        lineno, value.name,
+                        self.scope[value.name].source.lineno)
+
+        if not isinstance(self.scope, ClassScope):
+            for scope in self.scopeStack[::-1]:
+                existing = scope.get(value.name)
+                if (isinstance(existing, Importation)
+                        and not existing.used
+                        and (not isinstance(value, Importation) \
+                             or value.fullName == existing.fullName)
+                        and reportRedef):
+
+                    self.report(messages.RedefinedWhileUnused,
+                                lineno, value.name,
+                                scope[value.name].source.lineno)
+
+        if isinstance(value, UnBinding):
+            try:
+                del self.scope[value.name]
+            except KeyError:
+                self.report(messages.UndefinedName, lineno, value.name)
+        else:
+            self.scope[value.name] = value
+
+    def GLOBAL(self, node):
+        """
+        Keep track of globals declarations.
+        """
+        if isinstance(self.scope, FunctionScope):
+            self.scope.globals.update(dict.fromkeys(node.names))
+
+    def LISTCOMP(self, node):
+        # handle generators before element
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.elt, node)
+
+    GENERATOREXP = SETCOMP = LISTCOMP
+
+    # dictionary comprehensions; introduced in Python 2.7
+    def DICTCOMP(self, node):
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.key, node)
+        self.handleNode(node.value, node)
+
+    def FOR(self, node):
+        """
+        Process bindings for loop variables.
+        """
+        vars = []
+
+        def collectLoopVars(n):
+            if isinstance(n, _ast.Name):
+                vars.append(n.id)
+            elif isinstance(n, _ast.expr_context):
+                return
+            else:
+                for c in iter_child_nodes(n):
+                    collectLoopVars(c)
+
+        collectLoopVars(node.target)
+        for varn in vars:
+            if (isinstance(self.scope.get(varn), Importation)
+                    # unused ones will get an unused import warning
+                    and self.scope[varn].used):
+                self.report(messages.ImportShadowedByLoopVar,
+                            node.lineno, varn, self.scope[varn].source.lineno)
+
+        self.handleChildren(node)
+
+    def NAME(self, node):
+        """
+        Handle occurrence of Name (which can be a load/store/delete access.)
+        """
+        # Locate the name in locals / function / globals scopes.
+        if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)):
+            # try local scope
+            importStarred = self.scope.importStarred
+            try:
+                self.scope[node.id].used = (self.scope, node.lineno)
+            except KeyError:
+                pass
+            else:
+                return
+
+            # try enclosing function scopes
+
+            for scope in self.scopeStack[-2:0:-1]:
+                importStarred = importStarred or scope.importStarred
+                if not isinstance(scope, FunctionScope):
+                    continue
+                try:
+                    scope[node.id].used = (self.scope, node.lineno)
+                except KeyError:
+                    pass
+                else:
+                    return
+
+            # try global scope
+
+            importStarred = importStarred or self.scopeStack[0].importStarred
+            try:
+                self.scopeStack[0][node.id].used = (self.scope, node.lineno)
+            except KeyError:
+                if ((not hasattr(__builtin__, node.id))
+                        and node.id not in _MAGIC_GLOBALS
+                        and not importStarred):
+                    if (os.path.basename(self.filename) == '__init__.py' and
+                        node.id == '__path__'):
+                        # the special name __path__ is valid only in packages
+                        pass
+                    else:
+                        self.report(messages.UndefinedName,
+                                    node.lineno,
+                                    node.id)
+
+        elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)):
+            # if the name hasn't already been defined in the current scope
+            if isinstance(self.scope, FunctionScope) and \
+               node.id not in self.scope:
+                # for each function or module scope above us
+                for scope in self.scopeStack[:-1]:
+                    if not isinstance(scope, (FunctionScope, ModuleScope)):
+                        continue
+                    # if the name was defined in that scope, and the name has
+                    # been accessed already in the current scope, and hasn't
+                    # been declared global
+                    if (node.id in scope
+                            and scope[node.id].used
+                            and scope[node.id].used[0] is self.scope
+                            and node.id not in self.scope.globals):
+                        # then it's probably a mistake
+                        self.report(messages.UndefinedLocal,
+                                    scope[node.id].used[1],
+                                    node.id,
+                                    scope[node.id].source.lineno)
+                        break
+
+            if isinstance(node.parent,
+                          (_ast.For,
+                           _ast.comprehension,
+                           _ast.Tuple,
+                           _ast.List)):
+                binding = Binding(node.id, node)
+            elif (node.id == '__all__' and
+                  isinstance(self.scope, ModuleScope)):
+                binding = ExportBinding(node.id, node.parent.value)
+            else:
+                binding = Assignment(node.id, node)
+            if node.id in self.scope:
+                binding.used = self.scope[node.id].used
+            self.addBinding(node.lineno, binding)
+        elif isinstance(node.ctx, _ast.Del):
+            if isinstance(self.scope, FunctionScope) and \
+                   node.id in self.scope.globals:
+                del self.scope.globals[node.id]
+            else:
+                self.addBinding(node.lineno, UnBinding(node.id, node))
+        else:
+            # must be a Param context -- this only happens for names
+            # in function arguments, but these aren't dispatched through here
+            raise RuntimeError(
+                "Got impossible expression context: %r" % (node.ctx,))
+
+    def FUNCTIONDEF(self, node):
+        # the decorators attribute is called decorator_list as of Python 2.6
+        if hasattr(node, 'decorators'):
+            for deco in node.decorators:
+                self.handleNode(deco, node)
+        else:
+            for deco in node.decorator_list:
+                self.handleNode(deco, node)
+        self.addBinding(node.lineno, FunctionDefinition(node.name, node))
+        self.LAMBDA(node)
+
+    def LAMBDA(self, node):
+        for default in node.args.defaults:
+            self.handleNode(default, node)
+
+        def runFunction():
+            args = []
+
+            def addArgs(arglist):
+                for arg in arglist:
+                    if isinstance(arg, _ast.Tuple):
+                        addArgs(arg.elts)
+                    else:
+                        if arg.id in args:
+                            self.report(messages.DuplicateArgument,
+                                        node.lineno, arg.id)
+                        args.append(arg.id)
+
+            self.pushFunctionScope()
+            addArgs(node.args.args)
+            # vararg/kwarg identifiers are not Name nodes
+            if node.args.vararg:
+                args.append(node.args.vararg)
+            if node.args.kwarg:
+                args.append(node.args.kwarg)
+            for name in args:
+                self.addBinding(node.lineno, Argument(name, node),
+                                reportRedef=False)
+            if isinstance(node.body, list):
+                # case for FunctionDefs
+                for stmt in node.body:
+                    self.handleNode(stmt, node)
+            else:
+                # case for Lambdas
+                self.handleNode(node.body, node)
+
+            def checkUnusedAssignments():
+                """
+                Check to see if any assignments have not been used.
+                """
+                for name, binding in self.scope.iteritems():
+                    if (not binding.used and not name in self.scope.globals
+                        and isinstance(binding, Assignment)):
+                        self.report(messages.UnusedVariable,
+                                    binding.source.lineno, name)
+            self.deferAssignment(checkUnusedAssignments)
+            self.popScope()
+
+        self.deferFunction(runFunction)
+
+    def CLASSDEF(self, node):
+        """
+        Check names used in a class definition, including its decorators, base
+        classes, and the body of its definition.  Additionally, add its name to
+        the current scope.
+        """
+        # decorator_list is present as of Python 2.6
+        for deco in getattr(node, 'decorator_list', []):
+            self.handleNode(deco, node)
+        for baseNode in node.bases:
+            self.handleNode(baseNode, node)
+        self.pushClassScope()
+        for stmt in node.body:
+            self.handleNode(stmt, node)
+        self.popScope()
+        self.addBinding(node.lineno, Binding(node.name, node))
+
+    def ASSIGN(self, node):
+        self.handleNode(node.value, node)
+        for target in node.targets:
+            self.handleNode(target, node)
+
+    def AUGASSIGN(self, node):
+        # AugAssign is awkward: must set the context explicitly
+        # and visit twice, once with AugLoad context, once with
+        # AugStore context
+        node.target.ctx = _ast.AugLoad()
+        self.handleNode(node.target, node)
+        self.handleNode(node.value, node)
+        node.target.ctx = _ast.AugStore()
+        self.handleNode(node.target, node)
+
+    def IMPORT(self, node):
+        for alias in node.names:
+            name = alias.asname or alias.name
+            importation = Importation(name, node)
+            self.addBinding(node.lineno, importation)
+
+    def IMPORTFROM(self, node):
+        if node.module == '__future__':
+            if not self.futuresAllowed:
+                self.report(messages.LateFutureImport, node.lineno,
+                            [n.name for n in node.names])
+        else:
+            self.futuresAllowed = False
+
+        for alias in node.names:
+            if alias.name == '*':
+                self.scope.importStarred = True
+                self.report(messages.ImportStarUsed, node.lineno, node.module)
+                continue
+            name = alias.asname or alias.name
+            importation = Importation(name, node)
+            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
+

File flake8/run.py

+
+"""
+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, '<stdin>')
+    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()

File flake8/util.py

+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