Commits

Tarek Ziadé committed cdc887a Merge

merged branch python3

  • Participants
  • Parent commits fcd0603, acd26fa

Comments (0)

Files changed (9)

 lib
 include
 .*\.pyc$
+dist
+bin
+flake8.egg-info
+man
+\.Python

File flake8/mccabe.py

     http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html
     MIT License.
 """
-import compiler
+try:
+    from compiler import parse
+    iter_child_nodes = None
+except ImportError:
+    from ast import parse, iter_child_nodes
+
 import optparse
 import sys
+from collections import defaultdict
+
+
+class ASTVisitor:
+
+    VERBOSE = 0
+
+    def __init__(self):
+        self.node = None
+        self._cache = {}
+
+    def default(self, node, *args):
+        if hasattr(node, 'getChildNodes'):
+            children = node.getChildNodes()
+        else:
+            children = iter_child_nodes(node)
+
+        for child in children:
+            self.dispatch(child, *args)
+
+    def dispatch(self, node, *args):
+        self.node = node
+        klass = node.__class__
+        meth = self._cache.get(klass)
+        if meth is None:
+            className = klass.__name__
+            meth = getattr(self.visitor, 'visit' + className, self.default)
+            self._cache[klass] = meth
+
+        return meth(node, *args)
+
+    def preorder(self, tree, visitor, *args):
+        """Do preorder walk of tree using visitor"""
+        self.visitor = visitor
+        visitor.visit = self.dispatch
+        self.dispatch(tree, *args) # XXX *args make sense?
 
 
 class PathNode:
         self.look = look
 
     def to_dot(self):
-        print 'node [shape=%s,label="%s"] %d;' % \
-                (self.look, self.name, self.dot_id())
+        print('node [shape=%s,label="%s"] %d;' % \
+                (self.look, self.name, self.dot_id()))
 
     def dot_id(self):
         return id(self)
 class PathGraph:
     def __init__(self, name):
         self.name = name
-        self.nodes = {}
-
-    def add_node(self, n):
-        assert n
-        self.nodes.setdefault(n, [])
+        self.nodes = defaultdict(list)
 
     def connect(self, n1, n2):
-        assert n1
-        assert n2
-        self.nodes.setdefault(n1, []).append(n2)
+        self.nodes[n1].append(n2)
 
     def to_dot(self):
-        print 'subgraph {'
+        print('subgraph {')
         for node in self.nodes:
             node.to_dot()
         for node, nexts in self.nodes.items():
             for next in nexts:
-                print '%s -- %s;' % (node.dot_id(), next.dot_id())
-        print '}'
+                print('%s -- %s;' % (node.dot_id(), next.dot_id()))
+        print('}')
 
     def complexity(self):
         """ Return the McCabe complexity for the graph.
         return num_edges - num_nodes + 2
 
 
-class PathGraphingAstVisitor(compiler.visitor.ASTVisitor):
+class PathGraphingAstVisitor(ASTVisitor):
     """ A visitor for a parsed Abstract Syntax Tree which finds executable
         statements.
     """
 
     def __init__(self):
-        compiler.visitor.ASTVisitor.__init__(self)
+        ASTVisitor.__init__(self)
         self.classname = ""
         self.graphs = {}
         self.reset()
             self.graphs["%s%s" % (self.classname, node.name)] = self.graph
             self.reset()
 
+    visitFunctionDef = visitFunction
+
     def visitClass(self, node):
         old_classname = self.classname
         self.classname += node.name + "."
         if not self.tail:
             return
         pathnode = PathNode(name)
-        self.graph.add_node(pathnode)
         self.graph.connect(self.tail, pathnode)
         self.tail = pathnode
         return pathnode
     # TODO: visitTryFinally
     # TODO: visitWith
 
+    # XXX todo: determine which ones can add to the complexity
+    # py2
+    # TODO: visitStmt
+    # TODO: visitAssName
+    # TODO: visitCallFunc
+    # TODO: visitConst
+
+    # py3
+    # TODO: visitStore
+    # TODO: visitCall
+    # TODO: visitLoad
+    # TODO: visitNum
+    # TODO: visitarguments
+    # TODO: visitExpr
+
 
 def get_code_complexity(code, min=7, filename='stdin'):
     complex = []
     try:
-        ast = compiler.parse(code)
-    except AttributeError as e:
-        print >> sys.stderr, "Unable to parse %s: %s" % (filename, e)
+        ast = parse(code)
+    except AttributeError:
+        e = sys.exc_info()[1]
+        sys.stderr.write("Unable to parse %s: %s\n" % (filename, e))
         return 0
 
     visitor = PathGraphingAstVisitor()
     options, args = opar.parse_args(argv)
 
     text = open(args[0], "rU").read() + '\n\n'
-    ast = compiler.parse(text)
+    ast = parse(text)
     visitor = PathGraphingAstVisitor()
     visitor.preorder(ast, visitor)
 
     if options.dot:
-        print 'graph {'
+        print('graph {')
         for graph in visitor.graphs.values():
             if graph.complexity() >= options.min:
                 graph.to_dot()
-        print '}'
+        print('}')
     else:
         for graph in visitor.graphs.values():
             if graph.complexity() >= options.min:
-                print graph.name, graph.complexity()
+                print(graph.name, graph.complexity())
 
 
 if __name__ == '__main__':

File flake8/pep8.py

 
 """
 from flake8 import __version__ as flake8_version
-from pyflakes import __version__ as pep8_version
+from flake8.pyflakes import __version__ as pep8_version
 
 __version__ = '0.6.1'
 

File flake8/pyflakes.py

 # (c) 2005-2010 Divmod, Inc.
 # See LICENSE file for details
 
-import __builtin__
+try:
+    import __builtin__
+except ImportError:
+    import builtins as __builtin__
+
 import os.path
 import _ast
 import sys
                 all = []
 
             # Look for imported names that aren't used.
-            for importation in scope.itervalues():
+            for importation in scope.values():
                 if isinstance(importation, Importation):
                     if not importation.used and importation.name not in all:
                         self.report(
     def handleNode(self, node, parent):
         node.parent = parent
         if self.traceTree:
-            print '  ' * self.nodeDepth + node.__class__.__name__
+            print('  ' * self.nodeDepth + node.__class__.__name__)
         self.nodeDepth += 1
         if self.futuresAllowed and not \
                (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)):
         finally:
             self.nodeDepth -= 1
         if self.traceTree:
-            print '  ' * self.nodeDepth + 'end ' + node.__class__.__name__
+            print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
 
     def ignore(self, node):
         pass
     EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
 
     # additional node types
-    COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
+    COMPREHENSION = KEYWORD = handleChildren
+
+    def EXCEPTHANDLER(self, node):
+        self.scope[node.name] = node
 
     def addBinding(self, lineno, value, reportRedef=True):
         '''Called when a binding is altered.
                     if isinstance(arg, _ast.Tuple):
                         addArgs(arg.elts)
                     else:
-                        if arg.id in args:
+                        try:
+                            id_ = arg.id
+                        except AttributeError:
+                            id_ = arg.arg
+
+                        if id_ in args:
                             self.report(messages.DuplicateArgument,
-                                        node.lineno, arg.id)
-                        args.append(arg.id)
+                                        node.lineno, id_)
+                        args.append(id_)
 
             self.pushFunctionScope()
             addArgs(node.args.args)
                 """
                 Check to see if any assignments have not been used.
                 """
-                for name, binding in self.scope.iteritems():
+                for name, binding in self.scope.items():
                     if (not binding.used and not name in self.scope.globals
                         and isinstance(binding, Assignment)):
                         self.report(messages.UnusedVariable,
     @return: the number of warnings printed
     """
     try:
-        return check(file(filename, 'U').read() + '\n', filename)
-    except IOError, msg:
+        return check(open(filename, 'U').read() + '\n', filename)
+    except IOError:
+        msg = sys.exc_info()[1]
         print >> sys.stderr, "%s: %s" % (filename, msg.args[1])
         return 1
 
 
-def check(codeString, filename):
+def check(codeString, filename='(code)'):
     """
     Check the Python source given by C{codeString} for flakes.
 
     # First, compile into an AST and handle syntax errors.
     try:
         tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
-    except SyntaxError, value:
+    except SyntaxError:
+        value = sys.exc_info()[1]
         msg = value.args[0]
 
         (lineno, offset, text) = value.lineno, value.offset, value.text
     else:
         # Okay, it's syntactically valid.  Now check it.
         w = Checker(tree, filename)
-        w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
+        sorting = [(msg.lineno, msg) for msg in w.messages]
+        sorting.sort()
+        w.messages = [msg for index, msg in sorting]
         valid_warnings = 0
 
         for warning in w.messages:
             if skip_warning(warning):
                 continue
-            print warning
+            print(warning)
             valid_warnings += 1
 
         return valid_warnings

File flake8/run.py

 
 def _get_files(repo, **kwargs):
     seen = set()
-    for rev in xrange(repo[kwargs['node']], len(repo)):
+    for rev in range(repo[kwargs['node']], len(repo)):
         for file_ in repo[rev].files():
             file_ = os.path.join(repo.root, file_)
             if file_ in seen or not os.path.exists(file_):
         ext = os.path.splitext(filename)[-1]
         if ext != '.py':
             continue
+        if not os.path.exists(filename):
+            continue
         warnings += check_file(filename, complexity)
 
     if strict:

File flake8/tests/test_flakes.py

+from unittest import TestCase
+from flake8.pyflakes import check
+
+
+code = """
+try:
+    pass
+except ValueError as err:
+    print(err)
+"""
+
+
+class TestFlake(TestCase):
+
+    def test_exception(self):
+        warnings = check(code)
+        self.assertEqual(warnings, 0)

File flake8/tests/test_mccabe.py

 import unittest
 import sys
-from StringIO import StringIO
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
 
 from flake8.mccabe import get_code_complexity
 
         self.assertEqual(get_code_complexity(_GLOBAL, 1), 2)
         self.out.seek(0)
         res = self.out.read().strip().split('\n')
-        wanted = ["stdin:5:1: 'a' is too complex (3)",
-                  'stdin:Loop 2 is too complex (1)']
+        wanted = ["stdin:5:1: 'a' is too complex (4)",
+                  'stdin:Loop 2 is too complex (2)']
         self.assertEqual(res, wanted)

File flake8/util.py

 import re
+import os
 
 
 def skip_warning(warning):
     # XXX quick dirty hack, just need to keep the line in the warning
+    if not os.path.isfile(warning.filename):
+        return False
     line = open(warning.filename).readlines()[warning.lineno - 1]
     return skip_line(line)
 
-try:
-    from setuptools import setup
-except ImportError:
-    from distutils.core import setup   # NOQA
+import sys
+
+ispy3 = sys.version_info[0] == 3
+
+if ispy3:
+    from distutils.core import setup
+else:
+    try:
+        from setuptools import setup    # NOQA
+    except ImportError:
+        from distutils.core import setup   # NOQA
 
 from flake8 import __version__