1. Tarek Ziadé
  2. flake8

Commits

Ian Cordasco  committed 8885f46 Merge

Merge branch flint-merge-mccabe-plugin

  • Participants
  • Parent commits b590a04, 1338320
  • Branches flint-merge

Comments (0)

Files changed (5)

File LICENSE

View file
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
-
-
-
-== McCabe License (MIT) ==
-
-Copyright (C) <year> Ned Batchelder
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-== PyFlakes license (MIT) ==
-
-Copyright (c) 2005 Divmod, Inc., http://www.divmod.com/
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-== pep8 license (expat) ==
-
-Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-

File MANIFEST.in

View file
+include *.rst
 include CONTRIBUTORS.txt
-include README
 include LICENSE
 recursive-include scripts flake8.*

File flake8/mccabe.py

-""" Meager code path measurement tool.
-    Ned Batchelder
-    http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html
-    MIT License.
-"""
-from __future__ import with_statement
-
-import optparse
-import sys
-from collections import defaultdict
-
-from flake8.util import ast, iter_child_nodes
-
-version = 0.1
-
-
-class ASTVisitor(object):
-    """Performs a depth-first walk of the AST."""
-
-    def __init__(self):
-        self.node = None
-        self._cache = {}
-
-    def default(self, node, *args):
-        for child in iter_child_nodes(node):
-            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(object):
-    def __init__(self, name, look="circle"):
-        self.name = name
-        self.look = look
-
-    def to_dot(self):
-        print('node [shape=%s,label="%s"] %d;' % (
-            self.look, self.name, self.dot_id()))
-
-    def dot_id(self):
-        return id(self)
-
-
-class PathGraph(object):
-    def __init__(self, name, entity, lineno):
-        self.name = name
-        self.entity = entity
-        self.lineno = lineno
-        self.nodes = defaultdict(list)
-
-    def connect(self, n1, n2):
-        self.nodes[n1].append(n2)
-
-    def to_dot(self):
-        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('}')
-
-    def complexity(self):
-        """ Return the McCabe complexity for the graph.
-            V-E+2
-        """
-        num_edges = sum([len(n) for n in self.nodes.values()])
-        num_nodes = len(self.nodes)
-        return num_edges - num_nodes + 2
-
-
-class PathGraphingAstVisitor(ASTVisitor):
-    """ A visitor for a parsed Abstract Syntax Tree which finds executable
-        statements.
-    """
-
-    def __init__(self):
-        super(PathGraphingAstVisitor, self).__init__()
-        self.classname = ""
-        self.graphs = {}
-        self.reset()
-
-    def reset(self):
-        self.graph = None
-        self.tail = None
-
-    def dispatch_list(self, node_list):
-        for node in node_list:
-            self.dispatch(node)
-
-    def visitFunctionDef(self, node):
-
-        if self.classname:
-            entity = '%s%s' % (self.classname, node.name)
-        else:
-            entity = node.name
-
-        name = '%d:1: %r' % (node.lineno, entity)
-
-        if self.graph is not None:
-            # closure
-            pathnode = self.appendPathNode(name)
-            self.tail = pathnode
-            self.dispatch_list(node.body)
-            bottom = PathNode("", look='point')
-            self.graph.connect(self.tail, bottom)
-            self.graph.connect(pathnode, bottom)
-            self.tail = bottom
-        else:
-            self.graph = PathGraph(name, entity, node.lineno)
-            pathnode = PathNode(name)
-            self.tail = pathnode
-            self.dispatch_list(node.body)
-            self.graphs["%s%s" % (self.classname, node.name)] = self.graph
-            self.reset()
-
-    def visitClassDef(self, node):
-        old_classname = self.classname
-        self.classname += node.name + "."
-        self.dispatch_list(node.body)
-        self.classname = old_classname
-
-    def appendPathNode(self, name):
-        if not self.tail:
-            return
-        pathnode = PathNode(name)
-        self.graph.connect(self.tail, pathnode)
-        self.tail = pathnode
-        return pathnode
-
-    def visitSimpleStatement(self, node):
-        if node.lineno is None:
-            lineno = 0
-        else:
-            lineno = node.lineno
-        name = "Stmt %d" % lineno
-        self.appendPathNode(name)
-
-    visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \
-        visitRaise = visitYield = visitImport = visitCall = visitSubscript = \
-        visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \
-        visitSimpleStatement
-
-    def visitLoop(self, node):
-        name = "Loop %d" % node.lineno
-
-        if self.graph is None:
-            # global loop
-            self.graph = PathGraph(name, name, node.lineno)
-            pathnode = PathNode(name)
-            self.tail = pathnode
-            self.dispatch_list(node.body)
-            self.graphs["%s%s" % (self.classname, name)] = self.graph
-            self.reset()
-        else:
-            pathnode = self.appendPathNode(name)
-            self.tail = pathnode
-            self.dispatch_list(node.body)
-            bottom = PathNode("", look='point')
-            self.graph.connect(self.tail, bottom)
-            self.graph.connect(pathnode, bottom)
-            self.tail = bottom
-
-        # TODO: else clause in node.orelse
-
-    visitFor = visitWhile = visitLoop
-
-    def visitIf(self, node):
-        name = "If %d" % node.lineno
-        pathnode = self.appendPathNode(name)
-        loose_ends = []
-        self.dispatch_list(node.body)
-        loose_ends.append(self.tail)
-        if node.orelse:
-            self.tail = pathnode
-            self.dispatch_list(node.orelse)
-            loose_ends.append(self.tail)
-        else:
-            loose_ends.append(pathnode)
-        if pathnode:
-            bottom = PathNode("", look='point')
-            for le in loose_ends:
-                self.graph.connect(le, bottom)
-            self.tail = bottom
-
-    def visitTryExcept(self, node):
-        name = "TryExcept %d" % node.lineno
-        pathnode = self.appendPathNode(name)
-        loose_ends = []
-        self.dispatch_list(node.body)
-        loose_ends.append(self.tail)
-        for handler in node.handlers:
-            self.tail = pathnode
-            self.dispatch_list(handler.body)
-            loose_ends.append(self.tail)
-        if pathnode:
-            bottom = PathNode("", look='point')
-            for le in loose_ends:
-                self.graph.connect(le, bottom)
-            self.tail = bottom
-
-    def visitWith(self, node):
-        name = "With %d" % node.lineno
-        self.appendPathNode(name)
-        self.dispatch_list(node.body)
-
-
-class McCabeChecker(object):
-    """McCabe cyclomatic complexity checker."""
-    name = 'mccabe'
-    version = version
-    _code = 'C901'
-    _error_tmpl = "C901 %r is too complex (%d)"
-    max_complexity = 0
-
-    def __init__(self, tree, filename):
-        self.tree = tree
-
-    @classmethod
-    def add_options(cls, parser):
-        parser.add_option('--max-complexity', default=-1, action='store',
-                          type='int', help="McCabe complexity threshold")
-        parser.config_options.append('max-complexity')
-
-    @classmethod
-    def parse_options(cls, options):
-        cls.max_complexity = options.max_complexity
-
-    def run(self):
-        if self.max_complexity < 0:
-            return
-        visitor = PathGraphingAstVisitor()
-        visitor.preorder(self.tree, visitor)
-        for graph in visitor.graphs.values():
-            graph_complexity = graph.complexity()
-            if graph_complexity >= self.max_complexity:
-                text = self._error_tmpl % (graph.entity, graph_complexity)
-                yield graph.lineno, 0, text, type(self)
-
-
-def get_code_complexity(code, min_complexity=7, filename='stdin'):
-    try:
-        tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST)
-    except SyntaxError:
-        e = sys.exc_info()[1]
-        sys.stderr.write("Unable to parse %s: %s\n" % (filename, e))
-        return 0
-
-    complx = []
-    McCabeChecker.max_complexity = min_complexity
-    for lineno, offset, text, check in McCabeChecker(tree, filename).run():
-        complx.append('%s:%d:1: %s' % (filename, lineno, text))
-
-    if len(complx) == 0:
-        return 0
-    print('\n'.join(complx))
-    return len(complx)
-
-
-def get_module_complexity(module_path, min_complexity=7):
-    """Returns the complexity of a module"""
-    with open(module_path, "rU") as mod:
-        code = mod.read()
-    return get_code_complexity(code, min_complexity, filename=module_path)
-
-
-def main(argv):
-    opar = optparse.OptionParser()
-    opar.add_option("-d", "--dot", dest="dot",
-                    help="output a graphviz dot file", action="store_true")
-    opar.add_option("-m", "--min", dest="min",
-                    help="minimum complexity for output", type="int",
-                    default=2)
-
-    options, args = opar.parse_args(argv)
-
-    with open(args[0], "rU") as mod:
-        code = mod.read()
-    tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST)
-    visitor = PathGraphingAstVisitor()
-    visitor.preorder(tree, visitor)
-
-    if options.dot:
-        print('graph {')
-        for graph in visitor.graphs.values():
-            if graph.complexity() >= options.min:
-                graph.to_dot()
-        print('}')
-    else:
-        for graph in visitor.graphs.values():
-            if graph.complexity() >= options.min:
-                print(graph.name, graph.complexity())
-
-
-if __name__ == '__main__':
-    main(sys.argv[1:])

File flake8/tests/test_mccabe.py

-# -*- coding: utf-8 -*-
-import unittest
-import sys
-try:
-    from StringIO import StringIO
-except ImportError:
-    from io import StringIO     # NOQA
-
-from flake8.mccabe import get_code_complexity
-
-
-_GLOBAL = """\
-
-for i in range(10):
-    pass
-
-def a():
-    def b():
-        def c():
-            pass
-        c()
-    b()
-
-"""
-
-
-class McCabeTest(unittest.TestCase):
-
-    def setUp(self):
-        self.old = sys.stdout
-        self.out = sys.stdout = StringIO()
-
-    def tearDown(self):
-        sys.sdtout = self.old
-
-    def test_sample(self):
-        self.assertEqual(get_code_complexity(_GLOBAL, 1), 2)
-        self.out.seek(0)
-        res = self.out.read().strip().split('\n')
-        wanted = ["stdin:5:1: C901 'a' is too complex (4)",
-                  "stdin:2:1: C901 'Loop 2' is too complex (2)"]
-        self.assertEqual(res, wanted)

File setup.py

View file
         "setuptools",
         "pyflakes >= 0.6.1",
         "pep8 >= 1.4.2",
+        "mccabe >= 0.2a0",
     ],
     entry_points={
         'distutils.commands': ['flake8 = flake8.main:Flake8Command'],
         'console_scripts': ['flake8 = flake8.main:main'],
         'flake8.extension': [
             'F = flake8._pyflakes:FlakesChecker',
-            'C90 = flake8.mccabe:McCabeChecker',
         ],
     },
     classifiers=[