Added a line complexity checker to the code style checker.

Sat, 25 Mar 2017 17:36:50 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 25 Mar 2017 17:36:50 +0100
changeset 5661
ae4f5cdc3d00
parent 5660
4dabc5e36b18
child 5662
2c99fa1072b6

Added a line complexity checker to the code style checker.

Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.ui file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/CodeStyleChecker/ComplexityChecker.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/CodeStyleChecker/McCabeChecker.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/CodeStyleChecker/translations.py file | annotate | diff | comparison | revisions
changelog file | annotate | diff | comparison | revisions
--- a/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Sat Mar 25 17:18:12 2017 +0100
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Sat Mar 25 17:36:50 2017 +0100
@@ -18,7 +18,8 @@
 
 from DocStyleChecker import DocStyleChecker
 from MiscellaneousChecker import MiscellaneousChecker
-from McCabeChecker import McCabeChecker
+# TODO: rename the following module
+from ComplexityChecker import ComplexityChecker
 
 
 def initService():
@@ -188,16 +189,16 @@
     @param args arguments used by the codeStyleCheck function (list of
         excludeMessages (str), includeMessages (str), repeatMessages
         (bool), fixCodes (str), noFixCodes (str), fixIssues (bool),
-        maxLineLength (int), hangClosing (bool), docType (str), maximum
-        allowed code complexity (int), dictionary with arguments for the
-        miscellaneous checker (dict), errors (list of str), eol (str),
-        encoding (str), backup (bool))
+        maxLineLength (int), hangClosing (bool), docType (str), dictionary
+        with arguments for the code complexity checker (dict), dictionary
+        with arguments for the miscellaneous checker (dict), errors (list
+        of str), eol (str), encoding (str), backup (bool))
     @return tuple of statistics (dict) and results (tuple for each found
         violation of style (tuple of lineno (int), position (int), text (str),
         ignored (bool), fixed (bool), autofixing (bool), fixedMsg (str)))
     """
     (excludeMessages, includeMessages, repeatMessages, fixCodes, noFixCodes,
-     fixIssues, maxLineLength, hangClosing, docType, maxComplexity,
+     fixIssues, maxLineLength, hangClosing, docType, codeComplexityArgs,
      miscellaneousArgs, errors, eol, encoding, backup) = args
     
     stats = {}
@@ -259,12 +260,12 @@
         stats.update(miscellaneousChecker.counters)
         errors += miscellaneousChecker.errors
         
-        # check code complexity iaw. McCabe
-        mccabeChecker = McCabeChecker(
-            source, filename, select, ignore, maxComplexity)
-        mccabeChecker.run()
-        stats.update(mccabeChecker.counters)
-        errors += mccabeChecker.errors
+        # check code complexity
+        complexityChecker = ComplexityChecker(
+            source, filename, select, ignore, codeComplexityArgs)
+        complexityChecker.run()
+        stats.update(complexityChecker.counters)
+        errors += complexityChecker.errors
     
     errorsDict = {}
     for fname, lineno, position, text in errors:
--- a/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py	Sat Mar 25 17:18:12 2017 +0100
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py	Sat Mar 25 17:36:50 2017 +0100
@@ -65,6 +65,8 @@
         self.setupUi(self)
         self.setWindowFlags(Qt.Window)
         
+        self.optionsTabWidget.setCurrentIndex(0)
+        
         self.excludeMessagesSelectButton.setIcon(
             UI.PixmapCache.getIcon("select.png"))
         self.includeMessagesSelectButton.setIcon(
@@ -323,6 +325,10 @@
             self.__data["ShowIgnored"] = False
         if "MaxCodeComplexity" not in self.__data:
             self.__data["MaxCodeComplexity"] = 10
+        if "LineComplexity" not in self.__data:
+            self.__data["LineComplexity"] = 15
+        if "LineComplexityScore" not in self.__data:
+            self.__data["LineComplexityScore"] = 10
         if "ValidEncodings" not in self.__data:
             self.__data["ValidEncodings"] = "latin-1, utf-8"
         if "CopyrightMinFileSize" not in self.__data or \
@@ -350,6 +356,9 @@
         self.docTypeComboBox.setCurrentIndex(
             self.docTypeComboBox.findData(self.__data["DocstringType"]))
         self.complexitySpinBox.setValue(self.__data["MaxCodeComplexity"])
+        self.lineComplexitySpinBox.setValue(self.__data["LineComplexity"])
+        self.lineComplexityScoreSpinBox.setValue(
+            self.__data["LineComplexityScore"])
         self.encodingsEdit.setText(self.__data["ValidEncodings"])
         self.copyrightFileSizeSpinBox.setValue(
             self.__data["CopyrightMinFileSize"])
@@ -434,7 +443,11 @@
             hangClosing = self.hangClosingCheckBox.isChecked()
             docType = self.docTypeComboBox.itemData(
                 self.docTypeComboBox.currentIndex())
-            maxCodeComplexity = self.complexitySpinBox.value()
+            codeComplexityArgs = {
+                "McCabeComplexity": self.complexitySpinBox.value(),
+                "LineComplexity": self.lineComplexitySpinBox.value(),
+                "LineComplexityScore": self.lineComplexityScoreSpinBox.value(),
+            }
             miscellaneousArgs = {
                 "CodingChecker": self.encodingsEdit.text(),
                 "CopyrightChecker": {
@@ -447,7 +460,7 @@
             
             self.__options = [excludeMessages, includeMessages, repeatMessages,
                               fixCodes, noFixCodes, fixIssues, maxLineLength,
-                              hangClosing, docType, maxCodeComplexity,
+                              hangClosing, docType, codeComplexityArgs,
                               miscellaneousArgs]
             
             # now go through all the files
@@ -764,6 +777,8 @@
                 "DocstringType": self.docTypeComboBox.itemData(
                     self.docTypeComboBox.currentIndex()),
                 "MaxCodeComplexity": self.complexitySpinBox.value(),
+                "LineComplexity": self.lineComplexitySpinBox.value(),
+                "LineComplexityScore": self.lineComplexityScoreSpinBox.value(),
                 "ValidEncodings": self.encodingsEdit.text(),
                 "CopyrightMinFileSize": self.copyrightFileSizeSpinBox.value(),
                 "CopyrightAuthor": self.copyrightAuthorEdit.text(),
@@ -942,6 +957,12 @@
             Preferences.Prefs.settings.value("PEP8/DocstringType", "pep257")))
         self.complexitySpinBox.setValue(int(Preferences.Prefs.settings.value(
             "PEP8/MaxCodeComplexity", 10)))
+        self.lineComplexitySpinBox.setValue(
+            int(Preferences.Prefs.settings.value(
+                "PEP8/LineComplexity", 15)))
+        self.lineComplexityScoreSpinBox.setValue(
+            int(Preferences.Prefs.settings.value(
+                "PEP8/LineComplexityScore", 10)))
         self.encodingsEdit.setText(Preferences.Prefs.settings.value(
             "PEP8/ValidEncodings", "latin-1, utf-8"))
         self.copyrightFileSizeSpinBox.setValue(int(
@@ -988,6 +1009,11 @@
         Preferences.Prefs.settings.setValue(
             "PEP8/MaxCodeComplexity", self.complexitySpinBox.value())
         Preferences.Prefs.settings.setValue(
+            "PEP8/LineComplexity", self.lineComplexitySpinBox.value())
+        Preferences.Prefs.settings.setValue(
+            "PEP8/LineComplexityScore",
+            self.lineComplexityScoreSpinBox.value())
+        Preferences.Prefs.settings.setValue(
             "PEP8/ValidEncodings", self.encodingsEdit.text())
         Preferences.Prefs.settings.setValue(
             "PEP8/CopyrightMinFileSize", self.copyrightFileSizeSpinBox.value())
@@ -1017,6 +1043,8 @@
         Preferences.Prefs.settings.setValue("PEP8/HangClosing", False)
         Preferences.Prefs.settings.setValue("PEP8/DocstringType", "pep257")
         Preferences.Prefs.settings.setValue("PEP8/MaxCodeComplexity", 10)
+        Preferences.Prefs.settings.setValue("PEP8/LineComplexity", 15)
+        Preferences.Prefs.settings.setValue("PEP8/LineComplexityScore", 10)
         Preferences.Prefs.settings.setValue(
             "PEP8/ValidEncodings", "latin-1, utf-8")
         Preferences.Prefs.settings.setValue("PEP8/CopyrightMinFileSize", 0)
--- a/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.ui	Sat Mar 25 17:18:12 2017 +0100
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.ui	Sat Mar 25 17:36:50 2017 +0100
@@ -47,7 +47,7 @@
        <number>0</number>
       </property>
       <item>
-       <widget class="QTabWidget" name="tabWidget">
+       <widget class="QTabWidget" name="optionsTabWidget">
         <property name="currentIndex">
          <number>0</number>
         </property>
@@ -257,9 +257,9 @@
              <property name="geometry">
               <rect>
                <x>0</x>
-               <y>0</y>
+               <y>-400</y>
                <width>561</width>
-               <height>580</height>
+               <height>632</height>
               </rect>
              </property>
              <layout class="QVBoxLayout" name="verticalLayout_2">
@@ -544,36 +544,10 @@
               <item>
                <widget class="QGroupBox" name="groupBox_3">
                 <property name="title">
-                 <string>McCabe Complexity</string>
+                 <string>Code Complexity</string>
                 </property>
-                <layout class="QHBoxLayout" name="horizontalLayout_4">
-                 <item>
-                  <widget class="QLabel" name="label_8">
-                   <property name="text">
-                    <string>Max. Complexity:</string>
-                   </property>
-                  </widget>
-                 </item>
-                 <item>
-                  <widget class="QSpinBox" name="complexitySpinBox">
-                   <property name="statusTip">
-                    <string>Enter the maximum allowed code complexity (McCabe: 10)</string>
-                   </property>
-                   <property name="alignment">
-                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-                   </property>
-                   <property name="minimum">
-                    <number>0</number>
-                   </property>
-                   <property name="maximum">
-                    <number>100</number>
-                   </property>
-                   <property name="value">
-                    <number>10</number>
-                   </property>
-                  </widget>
-                 </item>
-                 <item>
+                <layout class="QGridLayout" name="gridLayout_4">
+                 <item row="0" column="2" rowspan="2">
                   <spacer name="horizontalSpacer_5">
                    <property name="orientation">
                     <enum>Qt::Horizontal</enum>
@@ -586,6 +560,75 @@
                    </property>
                   </spacer>
                  </item>
+                 <item row="0" column="0">
+                  <widget class="QLabel" name="label_8">
+                   <property name="text">
+                    <string>Max. McCabe Complexity:</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="0" column="1">
+                  <widget class="QSpinBox" name="complexitySpinBox">
+                   <property name="statusTip">
+                    <string>Enter the maximum allowed code complexity (McCabe: 10)</string>
+                   </property>
+                   <property name="alignment">
+                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+                   </property>
+                   <property name="maximum">
+                    <number>100</number>
+                   </property>
+                   <property name="value">
+                    <number>10</number>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="1" column="0">
+                  <widget class="QLabel" name="label_13">
+                   <property name="text">
+                    <string>Max. Line Complexity:</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="1" column="1">
+                  <widget class="QSpinBox" name="lineComplexitySpinBox">
+                   <property name="statusTip">
+                    <string>Enter the maximum complexity (number of nodes) for a line of code</string>
+                   </property>
+                   <property name="alignment">
+                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+                   </property>
+                   <property name="maximum">
+                    <number>100</number>
+                   </property>
+                   <property name="value">
+                    <number>15</number>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="0">
+                  <widget class="QLabel" name="label_14">
+                   <property name="text">
+                    <string>Max. Line Complexity Score:</string>
+                   </property>
+                  </widget>
+                 </item>
+                 <item row="2" column="1">
+                  <widget class="QSpinBox" name="lineComplexityScoreSpinBox">
+                   <property name="toolTip">
+                    <string>Enter the maximum allowed median for line complexity</string>
+                   </property>
+                   <property name="alignment">
+                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+                   </property>
+                   <property name="maximum">
+                    <number>100</number>
+                   </property>
+                   <property name="value">
+                    <number>10</number>
+                   </property>
+                  </widget>
+                 </item>
                 </layout>
                </widget>
               </item>
@@ -772,7 +815,7 @@
   </customwidget>
  </customwidgets>
  <tabstops>
-  <tabstop>tabWidget</tabstop>
+  <tabstop>optionsTabWidget</tabstop>
   <tabstop>excludeFilesEdit</tabstop>
   <tabstop>excludeMessagesEdit</tabstop>
   <tabstop>excludeMessagesSelectButton</tabstop>
@@ -797,6 +840,8 @@
   <tabstop>addBuiltinButton</tabstop>
   <tabstop>deleteBuiltinButton</tabstop>
   <tabstop>complexitySpinBox</tabstop>
+  <tabstop>lineComplexitySpinBox</tabstop>
+  <tabstop>lineComplexityScoreSpinBox</tabstop>
   <tabstop>startButton</tabstop>
   <tabstop>fixButton</tabstop>
   <tabstop>loadDefaultButton</tabstop>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/ComplexityChecker.py	Sat Mar 25 17:36:50 2017 +0100
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a checker for code complexity.
+"""
+
+import sys
+import ast
+
+from mccabe import PathGraphingAstVisitor
+
+
+class ComplexityChecker(object):
+    """
+    Class implementing a checker for code complexity.
+    """
+    Codes = [
+        "C101",
+        "C111", "C112",
+        
+        "C901",
+    ]
+
+    def __init__(self, source, filename, select, ignore, args):
+        """
+        Constructor
+        
+        @param source source code to be checked
+        @type list of str
+        @param filename name of the source file
+        @type str
+        @param select list of selected codes
+        @type list of str
+        @param ignore list of codes to be ignored
+        @type list of str
+        @param args dictionary of arguments for the miscellaneous checks
+        @type dict
+        """
+        self.__filename = filename
+        self.__source = source[:]
+        self.__select = tuple(select)
+        self.__ignore = ('',) if select else tuple(ignore)
+        self.__args = args
+        
+        self.__defaultArgs = {
+            "McCabeComplexity": 10,
+            "LineComplexity": 15,
+            "LineComplexityScore": 10,
+        }
+        
+        # statistics counters
+        self.counters = {}
+        
+        # collection of detected errors
+        self.errors = []
+        
+        checkersWithCodes = [
+            (self.__checkMcCabeComplexity, ("C101",)),
+            (self.__checkLineComplexity, ("C111", "C112")),
+        ]
+        
+        self.__checkers = []
+        for checker, codes in checkersWithCodes:
+            if any(not (code and self.__ignoreCode(code))
+                    for code in codes):
+                self.__checkers.append(checker)
+    
+    def __ignoreCode(self, code):
+        """
+        Private method to check if the message code should be ignored.
+
+        @param code message code to check for
+        @type str
+        @return flag indicating to ignore the given code
+        @rtype bool
+        """
+        return (code.startswith(self.__ignore) and
+                not code.startswith(self.__select))
+    
+    def __error(self, lineNumber, offset, code, *args):
+        """
+        Private method to record an issue.
+        
+        @param lineNumber line number of the issue
+        @type int
+        @param offset position within line of the issue
+        @type int
+        @param code message code
+        @type str
+        @param args arguments for the message
+        @type list
+        """
+        if self.__ignoreCode(code):
+            return
+        
+        if code in self.counters:
+            self.counters[code] += 1
+        else:
+            self.counters[code] = 1
+        
+        if code:
+            # record the issue with one based line number
+            self.errors.append(
+                (self.__filename, lineNumber, offset, (code, args)))
+    
+    def __reportInvalidSyntax(self):
+        """
+        Private method to report a syntax error.
+        """
+        exc_type, exc = sys.exc_info()[:2]
+        if len(exc.args) > 1:
+            offset = exc.args[1]
+            if len(offset) > 2:
+                offset = offset[1:3]
+        else:
+            offset = (1, 0)
+        self.__error(offset[0] - 1, offset[1] or 0,
+                     'C901', exc_type.__name__, exc.args[0])
+    
+    def run(self):
+        """
+        Public method to check the given source for code complexity.
+        """
+        if not self.__filename or not self.__source:
+            # don't do anything, if essential data is missing
+            return
+        
+        if not self.__checkers:
+            # don't do anything, if no codes were selected
+            return
+        
+        try:
+            self.__tree = compile(''.join(self.__source), self.__filename,
+                                  'exec', ast.PyCF_ONLY_AST)
+        except (SyntaxError, TypeError):
+            self.__reportInvalidSyntax()
+            return
+        
+        for check in self.__checkers:
+            check()
+    
+    def __checkMcCabeComplexity(self):
+        """
+        Private method to check the McCabe code complexity.
+        """
+        try:
+            # create the AST again because it is modified by the checker
+            tree = compile(''.join(self.__source), self.__filename, 'exec',
+                           ast.PyCF_ONLY_AST)
+        except (SyntaxError, TypeError):
+            # compile errors are already reported by the run() method
+            return
+        
+        maxComplexity = self.__args.get("McCabeComplexity",
+                                        self.__defaultArgs["McCabeComplexity"])
+        
+        visitor = PathGraphingAstVisitor()
+        visitor.preorder(tree, visitor)
+        for graph in visitor.graphs.values():
+            if graph.complexity() > maxComplexity:
+                self.__error(graph.lineno, 0, "C101",
+                             graph.entity, graph.complexity())
+    
+    def __checkLineComplexity(self):
+        """
+        Private method to check the complexity of a single line of code and
+        the median line complexity of the source code.
+        
+        Complexity is defined as the number of AST nodes produced by a line
+        of code.
+        """
+        maxLineComplexity = self.__args.get(
+            "LineComplexity", self.__defaultArgs["LineComplexity"])
+        maxLineComplexityScore = self.__args.get(
+            "LineComplexityScore", self.__defaultArgs["LineComplexityScore"])
+        
+        visitor = LineComplexityVisitor()
+        visitor.visit(self.__tree)
+        
+        sortedItems = visitor.sortedList()
+        score = visitor.score()
+        
+        for line, complexity in sortedItems:
+            if complexity > maxLineComplexity:
+                self.__error(line, 0, "C111", complexity)
+        
+        if score > maxLineComplexityScore:
+            self.__error(0, 0, "C112", score)
+
+
+class LineComplexityVisitor(ast.NodeVisitor):
+    """
+    Class calculating the number of AST nodes per line of code
+    and the median nodes/line score.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(LineComplexityVisitor, self).__init__()
+        self.__count = {}
+    
+    def visit(self, node):
+        """
+        Public method to recursively visit all the nodes and add up the
+        instructions.
+        
+        @param node reference to the node
+        @type ast.AST
+        """
+        if hasattr(node, 'lineno'):
+            self.__count[node.lineno] = self.__count.get(node.lineno, 0) + 1
+        self.generic_visit(node)
+    
+    def sortedList(self):
+        """
+        Public method to get a sorted list of (line, nodes) tuples.
+        
+        @return sorted list of (line, nodes) tuples
+        @rtype list of tuple of (int,int)
+        """
+        lst = [(line, self.__count[line])
+               for line in sorted(self.__count.keys())]
+        return lst
+
+    def score(self):
+        """
+        Public method to calculate the median.
+        
+        @return median line complexity value
+        @rtype float
+        """
+        total = 0
+        for line in self.__count:
+            total += self.__count[line]
+
+        return self.__median(self.__count.values())
+    
+    def __median(self, lst):
+        """
+        Private method to determine the median of a list.
+        
+        @param lst list to determine the median for
+        @type list of int
+        @return median of the list
+        @rtype float
+        """
+        sortedList = sorted(lst)
+        listLength = len(lst)
+        medianIndex = (listLength - 1) // 2
+
+        if (listLength % 2):
+            return float(sortedList[medianIndex])
+        else:
+            return (
+                (sortedList[medianIndex] + sortedList[medianIndex + 1]) / 2.0
+            )
+
+#
+# eflag: noqa = M702
--- a/Plugins/CheckerPlugins/CodeStyleChecker/McCabeChecker.py	Sat Mar 25 17:18:12 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2015 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing a checker for code complexity.
-"""
-
-import sys
-import ast
-
-from mccabe import PathGraphingAstVisitor
-
-
-class McCabeChecker(object):
-    """
-    Class implementing a checker for code complexity iaw. McCabe.
-    """
-    Codes = [
-        "C101",
-        
-        "C901",
-    ]
-
-    def __init__(self, source, filename, select, ignore, maxComplexity=10):
-        """
-        Constructor
-        
-        @param source source code to be checked
-        @type list of str
-        @param filename name of the source file
-        @type str
-        @param select list of selected codes
-        @type list of str
-        @param ignore list of codes to be ignored
-        @type list of str
-        @param maxComplexity maximum allowed complexity value
-        @type int
-        """
-        self.__filename = filename
-        self.__source = source[:]
-        self.__maxComplexity = maxComplexity
-        self.__select = tuple(select)
-        self.__ignore = ('',) if select else tuple(ignore)
-        
-        # statistics counters
-        self.counters = {}
-        
-        # collection of detected errors
-        self.errors = []
-    
-    def __error(self, lineNumber, offset, code, *args):
-        """
-        Private method to record an issue.
-        
-        @param lineNumber line number of the issue
-        @type int
-        @param offset position within line of the issue
-        @type int
-        @param code message code
-        @type str
-        @param args arguments for the message
-        @type list
-        """
-        if code in self.counters:
-            self.counters[code] += 1
-        else:
-            self.counters[code] = 1
-        
-        if code:
-            # record the issue with one based line number
-            self.errors.append(
-                (self.__filename, lineNumber, offset, (code, args)))
-    
-    def __reportInvalidSyntax(self):
-        """
-        Private method to report a syntax error.
-        """
-        exc_type, exc = sys.exc_info()[:2]
-        if len(exc.args) > 1:
-            offset = exc.args[1]
-            if len(offset) > 2:
-                offset = offset[1:3]
-        else:
-            offset = (1, 0)
-        self.__error(offset[0] - 1, offset[1] or 0,
-                     'C901', exc_type.__name__, exc.args[0])
-    
-    def __ignoreCode(self, code):
-        """
-        Private method to check if the error code should be ignored.
-
-        @param code message code to check for (string)
-        @return flag indicating to ignore the given code (boolean)
-        """
-        return (code.startswith(self.__ignore) and
-                not code.startswith(self.__select))
-    
-    def run(self):
-        """
-        Public method to check the given source for code complexity.
-        """
-        if not self.__filename or not self.__source:
-            # don't do anything, if essential data is missing
-            return
-        
-        if self.__ignoreCode("C101"):
-            # don't do anything, if this should be ignored
-            return
-        
-        try:
-            tree = compile(''.join(self.__source), self.__filename, 'exec',
-                           ast.PyCF_ONLY_AST)
-        except (SyntaxError, TypeError):
-            self.__reportInvalidSyntax()
-            return
-        
-        visitor = PathGraphingAstVisitor()
-        visitor.preorder(tree, visitor)
-        for graph in visitor.graphs.values():
-            if graph.complexity() > self.__maxComplexity:
-                self.__error(graph.lineno, 0, "C101",
-                             graph.entity, graph.complexity())
-
-#
-# eflag: noqa = M702
--- a/Plugins/CheckerPlugins/CodeStyleChecker/translations.py	Sat Mar 25 17:18:12 2017 +0100
+++ b/Plugins/CheckerPlugins/CodeStyleChecker/translations.py	Sat Mar 25 17:36:50 2017 +0100
@@ -419,11 +419,16 @@
         "NamingStyleChecker",
         "names 'l', 'O' and 'I' should be avoided"),
 
-    # McCabe messages
+    # Code complexity messages
     "C101": QCoreApplication.translate(
-        "McCabeChecker", "'{0}' is too complex ({1})"),
+        "ComplexityChecker", "'{0}' is too complex ({1})"),
+    "C111": QCoreApplication.translate(
+        "ComplexityChecker", "source code line is too complex ({0})"),
+    "C112": QCoreApplication.translate(
+        "ComplexityChecker",
+        "overall source code line complexity is too high ({0})"),
     "C901": QCoreApplication.translate(
-        "McCabeChecker", "{0}: {1}"),
+        "ComplexityChecker", "{0}: {1}"),
     
     # Messages of the Miscellaneous Checker
     "M101": QCoreApplication.translate(
@@ -700,6 +705,8 @@
     "D232": ["public"],
     "D901": ["SyntaxError", "Invalid Syntax"],
     "C101": ["foo.bar", "42"],
+    "C111": [42],
+    "C112": [12.0],
     "C901": ["SyntaxError", "Invalid Syntax"],
     "M102": ["enc42"],
     "M131": ["list"],
--- a/changelog	Sat Mar 25 17:18:12 2017 +0100
+++ b/changelog	Sat Mar 25 17:36:50 2017 +0100
@@ -5,6 +5,7 @@
 - Checkers
   -- added checks for shadowed Python builtins and unneccessary comprehensions
      and generators to the code style checker
+  -- added a checker to check for lines of code of too high complexity
   -- added capability to place line flags (e.g. __IGNORE...) on the line
      following the one to be ignored
 - Debugger

eric ide

mercurial