[Python-modules-commits] [python-mccabe] 01/09: Import python-mccabe_0.5.2.orig.tar.gz

Ondřej Nový onovy at moszumanska.debian.org
Sun Aug 7 20:43:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

onovy pushed a commit to branch master
in repository python-mccabe.

commit 80fa828b637fd83b885732ce7484607e649e43f2
Author: Ondřej Nový <onovy at debian.org>
Date:   Sun Aug 7 22:28:16 2016 +0200

    Import python-mccabe_0.5.2.orig.tar.gz
---
 LICENSE                     |  25 ++++++
 MANIFEST.in                 |   3 +
 PKG-INFO                    |  61 +++++++++++--
 README.rst                  |  47 +++++++++-
 mccabe.egg-info/PKG-INFO    |  61 +++++++++++--
 mccabe.egg-info/SOURCES.txt |   4 +
 mccabe.py                   | 115 +++++++++++++++---------
 setup.cfg                   |   6 ++
 setup.py                    |  15 +++-
 test_mccabe.py              | 211 ++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 487 insertions(+), 61 deletions(-)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8fd356e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright © <year> Ned Batchelder
+Copyright © 2011-2013 Tarek Ziade <tarek at ziade.org>
+Copyright © 2013 Florent Xicluna <florent.xicluna at gmail.com>
+
+Licensed under the terms of the Expat License
+
+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.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..0a584e0
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include LICENSE
+include README.rst
+include test_mccabe.py
diff --git a/PKG-INFO b/PKG-INFO
index c6d4a24..19364d8 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: mccabe
-Version: 0.2.1
+Version: 0.5.2
 Summary: McCabe checker, plugin for flake8
-Home-page: https://github.com/flintwork/mccabe
-Author: Florent Xicluna
-Author-email: florent.xicluna at gmail.com
+Home-page: https://github.com/pycqa/mccabe
+Author: Ian Cordasco
+Author-email: graffatcolmingov at gmail.com
 License: Expat license
 Description: McCabe complexity checker
         =========================
@@ -55,7 +55,7 @@ Description: McCabe complexity checker
             ...
             coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)
         
-        This feature is quite useful to detect over-complex code. According to McCabe,
+        This feature is quite useful to detect over-complex code.  According to McCabe,
         anything that goes beyond 10 is too complex.
         
         
@@ -73,6 +73,49 @@ Description: McCabe complexity checker
         Changes
         -------
         
+        0.5.2 - 2016-07-31
+        ``````````````````
+        
+        * When opening files ourselves, make sure we always name the file variable
+        
+        0.5.1 - 2016-07-28
+        ``````````````````
+        
+        * Set default maximum complexity to -1 on the class itself
+        
+        0.5.0 - 2016-05-30
+        ``````````````````
+        
+        * PyCon 2016 PDX release
+        
+        * Add support for Flake8 3.0
+        
+        0.4.0 - 2016-01-27
+        ``````````````````
+        
+        * Stop testing on Python 3.2
+        
+        * Add support for async/await keywords on Python 3.5 from PEP 0492
+        
+        0.3.1 - 2015-06-14
+        ``````````````````
+        
+        * Include ``test_mccabe.py`` in releases.
+        
+        * Always coerce the ``max_complexity`` value from Flake8's entry-point to an
+          integer.
+        
+        0.3 - 2014-12-17
+        ````````````````
+        
+        * Computation was wrong: the mccabe complexity starts at 1, not 2.
+        
+        * The ``max-complexity`` value is now inclusive.  E.g.: if the
+          value is 10 and the reported complexity is 10, then it passes.
+        
+        * Add tests.
+        
+        
         0.2.1 - 2013-04-03
         ``````````````````
         
@@ -83,7 +126,7 @@ Description: McCabe complexity checker
         0.2 - 2013-02-22
         ````````````````
         
-        * Rename project ``flint-mccabe`` to ``mccabe``.
+        * Rename project to ``mccabe``.
         
         * Provide ``flake8.extension`` setuptools entry point.
         
@@ -98,13 +141,17 @@ Description: McCabe complexity checker
         
 Keywords: flake8 mccabe
 Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Console
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Software Development :: Quality Assurance
diff --git a/README.rst b/README.rst
index 7025637..d9401d1 100644
--- a/README.rst
+++ b/README.rst
@@ -47,7 +47,7 @@ higher that the value::
     ...
     coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)
 
-This feature is quite useful to detect over-complex code. According to McCabe,
+This feature is quite useful to detect over-complex code.  According to McCabe,
 anything that goes beyond 10 is too complex.
 
 
@@ -65,6 +65,49 @@ Links
 Changes
 -------
 
+0.5.2 - 2016-07-31
+``````````````````
+
+* When opening files ourselves, make sure we always name the file variable
+
+0.5.1 - 2016-07-28
+``````````````````
+
+* Set default maximum complexity to -1 on the class itself
+
+0.5.0 - 2016-05-30
+``````````````````
+
+* PyCon 2016 PDX release
+
+* Add support for Flake8 3.0
+
+0.4.0 - 2016-01-27
+``````````````````
+
+* Stop testing on Python 3.2
+
+* Add support for async/await keywords on Python 3.5 from PEP 0492
+
+0.3.1 - 2015-06-14
+``````````````````
+
+* Include ``test_mccabe.py`` in releases.
+
+* Always coerce the ``max_complexity`` value from Flake8's entry-point to an
+  integer.
+
+0.3 - 2014-12-17
+````````````````
+
+* Computation was wrong: the mccabe complexity starts at 1, not 2.
+
+* The ``max-complexity`` value is now inclusive.  E.g.: if the
+  value is 10 and the reported complexity is 10, then it passes.
+
+* Add tests.
+
+
 0.2.1 - 2013-04-03
 ``````````````````
 
@@ -75,7 +118,7 @@ Changes
 0.2 - 2013-02-22
 ````````````````
 
-* Rename project ``flint-mccabe`` to ``mccabe``.
+* Rename project to ``mccabe``.
 
 * Provide ``flake8.extension`` setuptools entry point.
 
diff --git a/mccabe.egg-info/PKG-INFO b/mccabe.egg-info/PKG-INFO
index c6d4a24..19364d8 100644
--- a/mccabe.egg-info/PKG-INFO
+++ b/mccabe.egg-info/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: mccabe
-Version: 0.2.1
+Version: 0.5.2
 Summary: McCabe checker, plugin for flake8
-Home-page: https://github.com/flintwork/mccabe
-Author: Florent Xicluna
-Author-email: florent.xicluna at gmail.com
+Home-page: https://github.com/pycqa/mccabe
+Author: Ian Cordasco
+Author-email: graffatcolmingov at gmail.com
 License: Expat license
 Description: McCabe complexity checker
         =========================
@@ -55,7 +55,7 @@ Description: McCabe complexity checker
             ...
             coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)
         
-        This feature is quite useful to detect over-complex code. According to McCabe,
+        This feature is quite useful to detect over-complex code.  According to McCabe,
         anything that goes beyond 10 is too complex.
         
         
@@ -73,6 +73,49 @@ Description: McCabe complexity checker
         Changes
         -------
         
+        0.5.2 - 2016-07-31
+        ``````````````````
+        
+        * When opening files ourselves, make sure we always name the file variable
+        
+        0.5.1 - 2016-07-28
+        ``````````````````
+        
+        * Set default maximum complexity to -1 on the class itself
+        
+        0.5.0 - 2016-05-30
+        ``````````````````
+        
+        * PyCon 2016 PDX release
+        
+        * Add support for Flake8 3.0
+        
+        0.4.0 - 2016-01-27
+        ``````````````````
+        
+        * Stop testing on Python 3.2
+        
+        * Add support for async/await keywords on Python 3.5 from PEP 0492
+        
+        0.3.1 - 2015-06-14
+        ``````````````````
+        
+        * Include ``test_mccabe.py`` in releases.
+        
+        * Always coerce the ``max_complexity`` value from Flake8's entry-point to an
+          integer.
+        
+        0.3 - 2014-12-17
+        ````````````````
+        
+        * Computation was wrong: the mccabe complexity starts at 1, not 2.
+        
+        * The ``max-complexity`` value is now inclusive.  E.g.: if the
+          value is 10 and the reported complexity is 10, then it passes.
+        
+        * Add tests.
+        
+        
         0.2.1 - 2013-04-03
         ``````````````````
         
@@ -83,7 +126,7 @@ Description: McCabe complexity checker
         0.2 - 2013-02-22
         ````````````````
         
-        * Rename project ``flint-mccabe`` to ``mccabe``.
+        * Rename project to ``mccabe``.
         
         * Provide ``flake8.extension`` setuptools entry point.
         
@@ -98,13 +141,17 @@ Description: McCabe complexity checker
         
 Keywords: flake8 mccabe
 Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Console
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Software Development :: Quality Assurance
diff --git a/mccabe.egg-info/SOURCES.txt b/mccabe.egg-info/SOURCES.txt
index d3d8176..ae3a707 100644
--- a/mccabe.egg-info/SOURCES.txt
+++ b/mccabe.egg-info/SOURCES.txt
@@ -1,6 +1,10 @@
+LICENSE
+MANIFEST.in
 README.rst
 mccabe.py
+setup.cfg
 setup.py
+test_mccabe.py
 mccabe.egg-info/PKG-INFO
 mccabe.egg-info/SOURCES.txt
 mccabe.egg-info/dependency_links.txt
diff --git a/mccabe.py b/mccabe.py
index 4ea34be..c40381a 100644
--- a/mccabe.py
+++ b/mccabe.py
@@ -7,6 +7,8 @@ from __future__ import with_statement
 
 import optparse
 import sys
+import tokenize
+
 from collections import defaultdict
 try:
     import ast
@@ -14,7 +16,7 @@ try:
 except ImportError:   # Python 2.5
     from flake8.util import ast, iter_child_nodes
 
-__version__ = '0.2.1'
+__version__ = '0.5.2'
 
 
 class ASTVisitor(object):
@@ -67,6 +69,8 @@ class PathGraph(object):
 
     def connect(self, n1, n2):
         self.nodes[n1].append(n2)
+        # Ensure that the destination node is always counted.
+        self.nodes[n2] = []
 
     def to_dot(self):
         print('subgraph {')
@@ -131,6 +135,8 @@ class PathGraphingAstVisitor(ASTVisitor):
             self.graphs["%s%s" % (self.classname, node.name)] = self.graph
             self.reset()
 
+    visitAsyncFunctionDef = visitFunctionDef
+
     def visitClassDef(self, node):
         old_classname = self.classname
         self.classname += node.name + "."
@@ -156,38 +162,41 @@ class PathGraphingAstVisitor(ASTVisitor):
     visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \
         visitRaise = visitYield = visitImport = visitCall = visitSubscript = \
         visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \
-        visitSimpleStatement
+        visitAwait = visitSimpleStatement
 
     def visitLoop(self, node):
         name = "Loop %d" % node.lineno
+        self._subgraph(node, name)
+
+    visitAsyncFor = visitFor = visitWhile = visitLoop
 
+    def visitIf(self, node):
+        name = "If %d" % node.lineno
+        self._subgraph(node, name)
+
+    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(name, name, node.lineno)
             pathnode = PathNode(name)
-            self.tail = pathnode
-            self.dispatch_list(node.body)
+            self._subgraph_parse(node, pathnode, extra_blocks)
             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
+            self._subgraph_parse(node, pathnode, extra_blocks)
 
-        # TODO: else clause in node.orelse
-
-    visitFor = visitWhile = visitLoop
-
-    def visitIf(self, node):
-        name = "If %d" % node.lineno
-        pathnode = self.appendPathNode(name)
+    def _subgraph_parse(self, node, pathnode, extra_blocks):
+        """parse the body and any `else` block of `if` and `for` statements"""
         loose_ends = []
+        self.tail = pathnode
         self.dispatch_list(node.body)
         loose_ends.append(self.tail)
+        for extra in extra_blocks:
+            self.tail = pathnode
+            self.dispatch_list(extra.body)
+            loose_ends.append(self.tail)
         if node.orelse:
             self.tail = pathnode
             self.dispatch_list(node.orelse)
@@ -202,25 +211,17 @@ class PathGraphingAstVisitor(ASTVisitor):
 
     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
+        self._subgraph(node, name, extra_blocks=node.handlers)
+
+    visitTry = visitTryExcept
 
     def visitWith(self, node):
         name = "With %d" % node.lineno
         self.appendPathNode(name)
         self.dispatch_list(node.body)
 
+    visitAsyncWith = visitWith
+
 
 class McCabeChecker(object):
     """McCabe cyclomatic complexity checker."""
@@ -228,20 +229,33 @@ class McCabeChecker(object):
     version = __version__
     _code = 'C901'
     _error_tmpl = "C901 %r is too complex (%d)"
-    max_complexity = 0
+    max_complexity = -1
 
     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')
+        flag = '--max-complexity'
+        kwargs = {
+            'default': -1,
+            'action': 'store',
+            'type': 'int',
+            'help': 'McCabe complexity threshold',
+            'parse_from_config': 'True',
+        }
+        config_opts = getattr(parser, 'config_options', None)
+        if isinstance(config_opts, list):
+            # Flake8 2.x
+            kwargs.pop('parse_from_config')
+            parser.add_option(flag, **kwargs)
+            parser.config_options.append('max-complexity')
+        else:
+            parser.add_option(flag, **kwargs)
 
     @classmethod
     def parse_options(cls, options):
-        cls.max_complexity = options.max_complexity
+        cls.max_complexity = int(options.max_complexity)
 
     def run(self):
         if self.max_complexity < 0:
@@ -249,7 +263,7 @@ class McCabeChecker(object):
         visitor = PathGraphingAstVisitor()
         visitor.preorder(self.tree, visitor)
         for graph in visitor.graphs.values():
-            if graph.complexity() >= self.max_complexity:
+            if graph.complexity() > self.max_complexity:
                 text = self._error_tmpl % (graph.entity, graph.complexity())
                 yield graph.lineno, 0, text, type(self)
 
@@ -280,18 +294,36 @@ def get_module_complexity(module_path, threshold=7):
     return get_code_complexity(code, threshold, filename=module_path)
 
 
-def main(argv):
+def _read(filename):
+    if (2, 5) < sys.version_info < (3, 0):
+        with open(filename, 'rU') as f:
+            return f.read()
+    elif (3, 0) <= sys.version_info < (4, 0):
+        """Read the source code."""
+        try:
+            with open(filename, 'rb') as f:
+                (encoding, _) = tokenize.detect_encoding(f.readline)
+        except (LookupError, SyntaxError, UnicodeError):
+            # Fall back if file encoding is improperly declared
+            with open(filename, encoding='latin-1') as f:
+                return f.read()
+        with open(filename, 'r', encoding=encoding) as f:
+            return f.read()
+
+
+def main(argv=None):
+    if argv is None:
+        argv = sys.argv[1:]
     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="threshold",
                     help="minimum complexity for output", type="int",
-                    default=2)
+                    default=1)
 
     options, args = opar.parse_args(argv)
 
-    with open(args[0], "rU") as mod:
-        code = mod.read()
+    code = _read(args[0])
     tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST)
     visitor = PathGraphingAstVisitor()
     visitor.preorder(tree, visitor)
@@ -299,7 +331,8 @@ def main(argv):
     if options.dot:
         print('graph {')
         for graph in visitor.graphs.values():
-            if graph.complexity() >= options.threshold:
+            if (not options.threshold or
+                    graph.complexity() >= options.threshold):
                 graph.to_dot()
         print('}')
     else:
diff --git a/setup.cfg b/setup.cfg
index 861a9f5..878645d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,9 @@
+[wheel]
+universal = 1
+
+[aliases]
+test = pytest
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
index af7c7d3..e59903d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 from __future__ import with_statement
+
 from setuptools import setup
 
 
@@ -26,26 +27,32 @@ setup(
     keywords='flake8 mccabe',
     author='Tarek Ziade',
     author_email='tarek at ziade.org',
-    maintainer='Florent Xicluna',
-    maintainer_email='florent.xicluna at gmail.com',
-    url='https://github.com/flintwork/mccabe',
+    maintainer='Ian Cordasco',
+    maintainer_email='graffatcolmingov at gmail.com',
+    url='https://github.com/pycqa/mccabe',
     license='Expat license',
     py_modules=['mccabe'],
     zip_safe=False,
+    setup_requires=['pytest-runner'],
+    tests_require=['pytest'],
     entry_points={
         'flake8.extension': [
             'C90 = mccabe:McCabeChecker',
         ],
     },
     classifiers=[
-        'Development Status :: 3 - Alpha',
+        'Development Status :: 5 - Production/Stable',
         'Environment :: Console',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: MIT License',
         'Operating System :: OS Independent',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
         'Topic :: Software Development :: Libraries :: Python Modules',
         'Topic :: Software Development :: Quality Assurance',
     ],
diff --git a/test_mccabe.py b/test_mccabe.py
new file mode 100644
index 0000000..44fb565
--- /dev/null
+++ b/test_mccabe.py
@@ -0,0 +1,211 @@
+import unittest
+import sys
+try:
+    from StringIO import StringIO
+except ImportError:
+    from io import StringIO
+
+import pytest
+
+import mccabe
+from mccabe import get_code_complexity
+
+
+# Snippets are put outside of testcases because of spacing issue that would
+# otherwise occur with triple quoted strings.
+trivial = 'def f(): pass'
+
+
+sequential = """\
+def f(n):
+    k = n + 4
+    s = k + n
+    return s
+"""
+
+
+sequential_unencapsulated = """\
+k = 2 + 4
+s = k + 3
+"""
+
+
+if_elif_else_dead_path = """\
+def f(n):
+    if n > 3:
+        return "bigger than three"
+    elif n > 4:
+        return "is never executed"
+    else:
+        return "smaller than or equal to three"
+"""
+
+
+for_loop = """\
+def f():
+    for i in range(10):
+        print(i)
+"""
+
+
+for_else = """\
+def f(mylist):
+    for i in mylist:
+        print(i)
+    else:
+        print(None)
+"""
+
+
+recursive = """\
+def f(n):
+    if n > 4:
+        return f(n - 1)
+    else:
+        return n
+"""
+
+
+nested_functions = """\
+def a():
+    def b():
+        def c():
+            pass
+        c()
+    b()
+"""
+
+try_else = """\
+try:
+    print(1)
+except TypeA:
+    print(2)
+except TypeB:
+    print(3)
+else:
+    print(4)
+"""
+
+async_keywords = """\
+async def foobar(a, b, c):
+    await whatever(a, b, c)
+    if await b:
+        pass
+
+    async with c:
+        pass
+
+    async for x in a:
+        pass
+"""
+
+
+def get_complexity_number(snippet, strio, max=0):
+    """Get the complexity number from the printed string."""
+    # Report from the lowest complexity number.
+    get_code_complexity(snippet, max)
+    strio_val = strio.getvalue()
+    if strio_val:
+        return int(strio_val.split()[-1].strip("()"))
+    else:
+        return None
+
+
+class McCabeTestCase(unittest.TestCase):
+    def setUp(self):
+        # If not assigned to sys.stdout then getvalue() won't capture anything.
+        self._orig_stdout = sys.stdout
+        sys.stdout = self.strio = StringIO()
+
+    def tearDown(self):
+        # https://mail.python.org/pipermail/tutor/2012-January/088031.html
+        self.strio.close()
+        sys.stdout = self._orig_stdout
+
+    def assert_complexity(self, snippet, max):
+        complexity = get_complexity_number(snippet, self.strio)
+        self.assertEqual(complexity, max)
+
+        # should have the same complexity when inside a function as well.
+        infunc = 'def f():\n    ' + snippet.replace('\n', '\n    ')
+        complexity = get_complexity_number(infunc, self.strio)
+        self.assertEqual(complexity, max)
+
+    def test_print_message(self):
+        get_code_complexity(sequential, 0)
+        printed_message = self.strio.getvalue()
+        self.assertEqual(printed_message,
+                         "stdin:1:1: C901 'f' is too complex (1)\n")
+
+    def test_sequential_snippet(self):
+        complexity = get_complexity_number(sequential, self.strio)
+        self.assertEqual(complexity, 1)
+
+    def test_sequential_unencapsulated_snippet(self):
+        complexity = get_complexity_number(sequential_unencapsulated,
+                                           self.strio)
+        self.assertEqual(complexity, None)
+
+    def test_if_elif_else_dead_path_snippet(self):
+        complexity = get_complexity_number(if_elif_else_dead_path, self.strio)
+        # Paths that will never be executed are counted!
+        self.assertEqual(complexity, 3)
+
+    def test_for_loop_snippet(self):
+        complexity = get_complexity_number(for_loop, self.strio)
+        # The for loop adds an execution path; sometimes it won't be run.
+        self.assertEqual(complexity, 2)
+
+    def test_for_else_snippet(self):
+        complexity = get_complexity_number(for_else, self.strio)
+        # The for loop doesn't add an execution path, but its `else` does
+        self.assertEqual(complexity, 2)
+
+    def test_recursive_snippet(self):
+        complexity = get_complexity_number(recursive, self.strio)
+        self.assertEqual(complexity, 2)
+
+    def test_nested_functions_snippet(self):
+        complexity = get_complexity_number(nested_functions, self.strio)
+        self.assertEqual(complexity, 3)
+
+    def test_trivial(self):
+        """The most-trivial program should pass a max-complexity=1 test"""
+        complexity = get_complexity_number(trivial, self.strio, max=1)
+        self.assertEqual(complexity, None)
+        printed_message = self.strio.getvalue()
+        self.assertEqual(printed_message, "")
+
+    def test_try_else(self):
+        self.assert_complexity(try_else, 4)
+
+    @pytest.mark.skipif(sys.version_info < (3, 5),
+                        reason="Async keywords are only valid on Python 3.5+")
+    def test_async_keywords(self):
+        """Validate that we properly process async keyword usage."""
+        complexity = get_complexity_number(async_keywords, self.strio)
+        self.assertEqual(complexity, 3)
+
+
+class RegressionTests(unittest.TestCase):
+    def setUp(self):
+        self.original_complexity = mccabe.McCabeChecker.max_complexity
+
+    def tearDown(self):
+        mccabe.McCabeChecker.max_complexity = self.original_complexity
+
+    def test_max_complexity_is_always_an_int(self):
+        """Ensure bug #32 does not regress."""
+        class _options(object):
+            max_complexity = None
+
+        options = _options()
+        options.max_complexity = '16'
+
+        self.assertEqual(0, mccabe.McCabeChecker.max_complexity)
+        mccabe.McCabeChecker.parse_options(options)
+        self.assertEqual(16, mccabe.McCabeChecker.max_complexity)
+
+
+if __name__ == "__main__":
+    unittest.main()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-mccabe.git



More information about the Python-modules-commits mailing list