mirror of
https://github.com/PyCQA/flake8.git
synced 2026-04-12 15:44:17 +00:00
fixed the mccabe complexity for py3 and py2 as well
This commit is contained in:
parent
1003eeabe8
commit
b29f197d69
2 changed files with 61 additions and 16 deletions
|
|
@ -4,14 +4,49 @@
|
||||||
MIT License.
|
MIT License.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from compiler.visitor import ASTVisitor as Visitor
|
|
||||||
from compiler import parse
|
from compiler import parse
|
||||||
|
iter_child_nodes = None
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from ast import NodeVisitor as Visitor
|
from ast import parse, iter_child_nodes
|
||||||
from ast import parse
|
|
||||||
|
|
||||||
import optparse
|
import optparse
|
||||||
import sys
|
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:
|
class PathNode:
|
||||||
|
|
@ -30,16 +65,10 @@ class PathNode:
|
||||||
class PathGraph:
|
class PathGraph:
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.nodes = {}
|
self.nodes = defaultdict(list)
|
||||||
|
|
||||||
def add_node(self, n):
|
|
||||||
assert n
|
|
||||||
self.nodes.setdefault(n, [])
|
|
||||||
|
|
||||||
def connect(self, n1, n2):
|
def connect(self, n1, n2):
|
||||||
assert n1
|
self.nodes[n1].append(n2)
|
||||||
assert n2
|
|
||||||
self.nodes.setdefault(n1, []).append(n2)
|
|
||||||
|
|
||||||
def to_dot(self):
|
def to_dot(self):
|
||||||
print('subgraph {')
|
print('subgraph {')
|
||||||
|
|
@ -59,13 +88,13 @@ class PathGraph:
|
||||||
return num_edges - num_nodes + 2
|
return num_edges - num_nodes + 2
|
||||||
|
|
||||||
|
|
||||||
class PathGraphingAstVisitor(Visitor):
|
class PathGraphingAstVisitor(ASTVisitor):
|
||||||
""" A visitor for a parsed Abstract Syntax Tree which finds executable
|
""" A visitor for a parsed Abstract Syntax Tree which finds executable
|
||||||
statements.
|
statements.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Visitor.__init__(self)
|
ASTVisitor.__init__(self)
|
||||||
self.classname = ""
|
self.classname = ""
|
||||||
self.graphs = {}
|
self.graphs = {}
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
@ -100,6 +129,8 @@ class PathGraphingAstVisitor(Visitor):
|
||||||
self.graphs["%s%s" % (self.classname, node.name)] = self.graph
|
self.graphs["%s%s" % (self.classname, node.name)] = self.graph
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
visitFunctionDef = visitFunction
|
||||||
|
|
||||||
def visitClass(self, node):
|
def visitClass(self, node):
|
||||||
old_classname = self.classname
|
old_classname = self.classname
|
||||||
self.classname += node.name + "."
|
self.classname += node.name + "."
|
||||||
|
|
@ -110,7 +141,6 @@ class PathGraphingAstVisitor(Visitor):
|
||||||
if not self.tail:
|
if not self.tail:
|
||||||
return
|
return
|
||||||
pathnode = PathNode(name)
|
pathnode = PathNode(name)
|
||||||
self.graph.add_node(pathnode)
|
|
||||||
self.graph.connect(self.tail, pathnode)
|
self.graph.connect(self.tail, pathnode)
|
||||||
self.tail = pathnode
|
self.tail = pathnode
|
||||||
return pathnode
|
return pathnode
|
||||||
|
|
@ -177,6 +207,21 @@ class PathGraphingAstVisitor(Visitor):
|
||||||
# TODO: visitTryFinally
|
# TODO: visitTryFinally
|
||||||
# TODO: visitWith
|
# 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'):
|
def get_code_complexity(code, min=7, filename='stdin'):
|
||||||
complex = []
|
complex = []
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,6 @@ class McCabeTest(unittest.TestCase):
|
||||||
self.assertEqual(get_code_complexity(_GLOBAL, 1), 2)
|
self.assertEqual(get_code_complexity(_GLOBAL, 1), 2)
|
||||||
self.out.seek(0)
|
self.out.seek(0)
|
||||||
res = self.out.read().strip().split('\n')
|
res = self.out.read().strip().split('\n')
|
||||||
wanted = ["stdin:5:1: 'a' is too complex (3)",
|
wanted = ["stdin:5:1: 'a' is too complex (4)",
|
||||||
'stdin:Loop 2 is too complex (1)']
|
'stdin:Loop 2 is too complex (2)']
|
||||||
self.assertEqual(res, wanted)
|
self.assertEqual(res, wanted)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue