summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages/pylint/extensions/mccabe.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/Lib/site-packages/pylint/extensions/mccabe.py')
-rw-r--r--venv/Lib/site-packages/pylint/extensions/mccabe.py196
1 files changed, 196 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/pylint/extensions/mccabe.py b/venv/Lib/site-packages/pylint/extensions/mccabe.py
new file mode 100644
index 0000000..cafac97
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/mccabe.py
@@ -0,0 +1,196 @@
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Module to add McCabe checker class for pylint. """
+
+from mccabe import PathGraph as Mccabe_PathGraph
+from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor
+
+from pylint import checkers
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import HIGH, IAstroidChecker
+
+
+class PathGraph(Mccabe_PathGraph):
+ def __init__(self, node):
+ super(PathGraph, self).__init__(name="", entity="", lineno=1)
+ self.root = node
+
+
+class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):
+ def __init__(self):
+ super(PathGraphingAstVisitor, self).__init__()
+ self._bottom_counter = 0
+
+ def default(self, node, *args):
+ for child in node.get_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:
+ class_name = klass.__name__
+ meth = getattr(self.visitor, "visit" + class_name, self.default)
+ self._cache[klass] = meth
+ return meth(node, *args)
+
+ def visitFunctionDef(self, node):
+ if self.graph is not None:
+ # closure
+ pathnode = self._append_node(node)
+ self.tail = pathnode
+ self.dispatch_list(node.body)
+ bottom = "%s" % self._bottom_counter
+ self._bottom_counter += 1
+ self.graph.connect(self.tail, bottom)
+ self.graph.connect(node, bottom)
+ self.tail = bottom
+ else:
+ self.graph = PathGraph(node)
+ self.tail = node
+ self.dispatch_list(node.body)
+ self.graphs["%s%s" % (self.classname, node.name)] = self.graph
+ self.reset()
+
+ visitAsyncFunctionDef = visitFunctionDef
+
+ def visitSimpleStatement(self, node):
+ self._append_node(node)
+
+ visitAssert = (
+ visitAssign
+ ) = (
+ visitAugAssign
+ ) = (
+ visitDelete
+ ) = (
+ visitPrint
+ ) = (
+ visitRaise
+ ) = (
+ visitYield
+ ) = (
+ visitImport
+ ) = (
+ visitCall
+ ) = (
+ visitSubscript
+ ) = (
+ visitPass
+ ) = (
+ visitContinue
+ ) = (
+ visitBreak
+ ) = visitGlobal = visitReturn = visitExpr = visitAwait = visitSimpleStatement
+
+ def visitWith(self, node):
+ self._append_node(node)
+ self.dispatch_list(node.body)
+
+ visitAsyncWith = visitWith
+
+ def _append_node(self, node):
+ if not self.tail:
+ return None
+ self.graph.connect(self.tail, node)
+ self.tail = node
+ return node
+
+ def _subgraph(self, node, name, extra_blocks=()):
+ """create the subgraphs representing any `if` and `for` statements"""
+ if self.graph is None:
+ # global loop
+ self.graph = PathGraph(node)
+ self._subgraph_parse(node, node, extra_blocks)
+ self.graphs["%s%s" % (self.classname, name)] = self.graph
+ self.reset()
+ else:
+ self._append_node(node)
+ self._subgraph_parse(node, node, extra_blocks)
+
+ def _subgraph_parse(self, node, pathnode, extra_blocks):
+ """parse the body and any `else` block of `if` and `for` statements"""
+ loose_ends = []
+ self.tail = node
+ self.dispatch_list(node.body)
+ loose_ends.append(self.tail)
+ for extra in extra_blocks:
+ self.tail = node
+ self.dispatch_list(extra.body)
+ loose_ends.append(self.tail)
+ if node.orelse:
+ self.tail = node
+ self.dispatch_list(node.orelse)
+ loose_ends.append(self.tail)
+ else:
+ loose_ends.append(node)
+ if node:
+ bottom = "%s" % self._bottom_counter
+ self._bottom_counter += 1
+ for end in loose_ends:
+ self.graph.connect(end, bottom)
+ self.tail = bottom
+
+
+class McCabeMethodChecker(checkers.BaseChecker):
+ """Checks McCabe complexity cyclomatic threshold in methods and functions
+ to validate a too complex code.
+ """
+
+ __implements__ = IAstroidChecker
+ name = "design"
+
+ msgs = {
+ "R1260": (
+ "%s is too complex. The McCabe rating is %d",
+ "too-complex",
+ "Used when a method or function is too complex based on "
+ "McCabe Complexity Cyclomatic",
+ )
+ }
+ options = (
+ (
+ "max-complexity",
+ {
+ "default": 10,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "McCabe complexity cyclomatic threshold",
+ },
+ ),
+ )
+
+ @check_messages("too-complex")
+ def visit_module(self, node):
+ """visit an astroid.Module node to check too complex rating and
+ add message if is greather than max_complexity stored from options"""
+ visitor = PathGraphingAstVisitor()
+ for child in node.body:
+ visitor.preorder(child, visitor)
+ for graph in visitor.graphs.values():
+ complexity = graph.complexity()
+ node = graph.root
+ if hasattr(node, "name"):
+ node_name = "'%s'" % node.name
+ else:
+ node_name = "This '%s'" % node.__class__.__name__.lower()
+ if complexity <= self.config.max_complexity:
+ continue
+ self.add_message(
+ "too-complex", node=node, confidence=HIGH, args=(node_name, complexity)
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(McCabeMethodChecker(linter))