Hello community, here is the log from the commit of package python-mccabe for openSUSE:Factory checked in at 2017-04-19 17:56:33 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-mccabe (Old) and /work/SRC/openSUSE:Factory/.python-mccabe.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-mccabe" Wed Apr 19 17:56:33 2017 rev:7 rq:479716 version:0.6.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-mccabe/python-mccabe.changes 2015-05-15 09:03:05.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-mccabe.new/python-mccabe.changes 2017-04-19 17:56:34.458798710 +0200 @@ -1,0 +2,46 @@ +Mon Mar 6 14:03:34 UTC 2017 - [email protected] + +- update for singlespec +- enable tests + +------------------------------------------------------------------- +Mon Feb 20 10:36:41 UTC 2017 - [email protected] + +- Use pypi.io and https in Source + +------------------------------------------------------------------- +Wed Feb 15 08:55:16 UTC 2017 - [email protected] + +- update to version 0.6.1: + * Fix signature for PathGraphingAstVisitor.default to match the + signature for ASTVisitor + +- update to version 0.6.0: + * Add support for Python 3.6 + * Fix handling for missing statement types + +- update to version 0.5.3: + * Report actual column number of violation instead of the start + of the line + +- update to version 0.5.2: + * When opening files ourselves, make sure we always name the file + variable + +- update to version 0.5.1: + * Set default maximum complexity to -1 on the class itself + +- update to version 0.5.0: + * PyCon 2016 PDX release + * Add support for Flake8 3.0 + +- update to version 0.4.0: + * Stop testing on Python 3.2 + * Add support for async/await keywords on Python 3.5 from PEP 0492 + +- update to version 0.3.1: + * Include test_mccabe.py in releases. + * Always coerce the max_complexity value from Flake8’s entry-point + to an integer. + +------------------------------------------------------------------- Old: ---- mccabe-0.3.tar.gz New: ---- mccabe-0.6.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-mccabe.spec ++++++ --- /var/tmp/diff_new_pack.7v64sO/_old 2017-04-19 17:56:35.194694598 +0200 +++ /var/tmp/diff_new_pack.7v64sO/_new 2017-04-19 17:56:35.198694033 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-mccabe # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,22 +16,21 @@ # +%{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-mccabe -Version: 0.3 +Version: 0.6.1 Release: 0 Summary: McCabe checker, plugin for flake8 License: MIT Group: Development/Languages/Python Url: https://github.com/flintwork/mccabe -Source: http://pypi.python.org/packages/source/m/mccabe/mccabe-%{version}.tar.gz -BuildRequires: python-devel -BuildRequires: python-setuptools +Source: https://pypi.io/packages/source/m/mccabe/mccabe-%{version}.tar.gz +BuildRequires: %{python_module pytest-runner} +BuildRequires: %{python_module setuptools} +BuildRequires: python-rpm-macros BuildRoot: %{_tmppath}/%{name}-%{version}-build -%if 0%{?suse_version} && 0%{?suse_version} <= 1110 -%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%else BuildArch: noarch -%endif +%python_subpackages %description Ned's script to check McCabe complexity. @@ -41,12 +40,15 @@ %setup -q -n mccabe-%{version} %build -python setup.py build +%python_build %install -python setup.py install --prefix=%{_prefix} --root=%{buildroot} +%python_install -%files +%check +%python_exec %{_bindir}/py.test + +%files %{python_files} %defattr(-,root,root,-) %doc LICENSE README.rst %{python_sitelib}/* ++++++ mccabe-0.3.tar.gz -> mccabe-0.6.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/MANIFEST.in new/mccabe-0.6.1/MANIFEST.in --- old/mccabe-0.3/MANIFEST.in 2014-04-25 22:47:01.000000000 +0200 +++ new/mccabe-0.6.1/MANIFEST.in 2017-01-23 13:24:16.000000000 +0100 @@ -1 +1,3 @@ -include LICENSE README.rst +include LICENSE +include README.rst +include test_mccabe.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/PKG-INFO new/mccabe-0.6.1/PKG-INFO --- old/mccabe-0.3/PKG-INFO 2014-12-17 02:23:10.000000000 +0100 +++ new/mccabe-0.6.1/PKG-INFO 2017-01-26 23:13:05.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: mccabe -Version: 0.3 +Version: 0.6.1 Summary: McCabe checker, plugin for flake8 -Home-page: https://github.com/flintwork/mccabe -Author: Florent Xicluna -Author-email: [email protected] +Home-page: https://github.com/pycqa/mccabe +Author: Ian Cordasco +Author-email: [email protected] License: Expat license Description: McCabe complexity checker ========================= @@ -73,6 +73,56 @@ Changes ------- + 0.6.1 - 2017-01-26 + `````````````````` + + * Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + + 0.6.0 - 2017-01-23 + `````````````````` + + * Add support for Python 3.6 + + * Fix handling for missing statement types + + 0.5.3 - 2016-12-14 + `````````````````` + + * Report actual column number of violation instead of the start of the line + + 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 ```````````````` @@ -109,13 +159,18 @@ 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: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/README.rst new/mccabe-0.6.1/README.rst --- old/mccabe-0.3/README.rst 2014-12-17 01:15:26.000000000 +0100 +++ new/mccabe-0.6.1/README.rst 2017-01-26 23:11:16.000000000 +0100 @@ -65,6 +65,56 @@ Changes ------- +0.6.1 - 2017-01-26 +`````````````````` + +* Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + +0.6.0 - 2017-01-23 +`````````````````` + +* Add support for Python 3.6 + +* Fix handling for missing statement types + +0.5.3 - 2016-12-14 +`````````````````` + +* Report actual column number of violation instead of the start of the line + +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 ```````````````` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/mccabe.egg-info/PKG-INFO new/mccabe-0.6.1/mccabe.egg-info/PKG-INFO --- old/mccabe-0.3/mccabe.egg-info/PKG-INFO 2014-12-17 02:23:10.000000000 +0100 +++ new/mccabe-0.6.1/mccabe.egg-info/PKG-INFO 2017-01-26 23:13:05.000000000 +0100 @@ -1,10 +1,10 @@ Metadata-Version: 1.1 Name: mccabe -Version: 0.3 +Version: 0.6.1 Summary: McCabe checker, plugin for flake8 -Home-page: https://github.com/flintwork/mccabe -Author: Florent Xicluna -Author-email: [email protected] +Home-page: https://github.com/pycqa/mccabe +Author: Ian Cordasco +Author-email: [email protected] License: Expat license Description: McCabe complexity checker ========================= @@ -73,6 +73,56 @@ Changes ------- + 0.6.1 - 2017-01-26 + `````````````````` + + * Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + + 0.6.0 - 2017-01-23 + `````````````````` + + * Add support for Python 3.6 + + * Fix handling for missing statement types + + 0.5.3 - 2016-12-14 + `````````````````` + + * Report actual column number of violation instead of the start of the line + + 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 ```````````````` @@ -109,13 +159,18 @@ 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: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/mccabe.egg-info/SOURCES.txt new/mccabe-0.6.1/mccabe.egg-info/SOURCES.txt --- old/mccabe-0.3/mccabe.egg-info/SOURCES.txt 2014-12-17 02:23:10.000000000 +0100 +++ new/mccabe-0.6.1/mccabe.egg-info/SOURCES.txt 2017-01-26 23:13:05.000000000 +0100 @@ -4,6 +4,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/mccabe.py new/mccabe-0.6.1/mccabe.py --- old/mccabe-0.3/mccabe.py 2014-12-17 02:21:04.000000000 +0100 +++ new/mccabe-0.6.1/mccabe.py 2017-01-26 23:10:14.000000000 +0100 @@ -7,6 +7,8 @@ import optparse import sys +import tokenize + from collections import defaultdict try: import ast @@ -14,7 +16,7 @@ except ImportError: # Python 2.5 from flake8.util import ast, iter_child_nodes -__version__ = '0.3' +__version__ = '0.6.1' class ASTVisitor(object): @@ -59,10 +61,11 @@ class PathGraph(object): - def __init__(self, name, entity, lineno): + def __init__(self, name, entity, lineno, column=0): self.name = name self.entity = entity self.lineno = lineno + self.column = column self.nodes = defaultdict(list) def connect(self, n1, n2): @@ -114,7 +117,7 @@ else: entity = node.name - name = '%d:1: %r' % (node.lineno, entity) + name = '%d:%d: %r' % (node.lineno, node.col_offset, entity) if self.graph is not None: # closure @@ -126,13 +129,15 @@ self.graph.connect(pathnode, bottom) self.tail = bottom else: - self.graph = PathGraph(name, entity, node.lineno) + self.graph = PathGraph(name, entity, node.lineno, node.col_offset) pathnode = PathNode(name) self.tail = pathnode self.dispatch_list(node.body) 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 + "." @@ -155,16 +160,17 @@ name = "Stmt %d" % lineno self.appendPathNode(name) - visitAssert = visitAssign = visitAugAssign = visitDelete = visitPrint = \ - visitRaise = visitYield = visitImport = visitCall = visitSubscript = \ - visitPass = visitContinue = visitBreak = visitGlobal = visitReturn = \ - visitSimpleStatement + def default(self, node, *args): + if isinstance(node, ast.stmt): + self.visitSimpleStatement(node) + else: + super(PathGraphingAstVisitor, self).default(node, *args) def visitLoop(self, node): name = "Loop %d" % node.lineno self._subgraph(node, name) - visitFor = visitWhile = visitLoop + visitAsyncFor = visitFor = visitWhile = visitLoop def visitIf(self, node): name = "If %d" % node.lineno @@ -174,7 +180,7 @@ """create the subgraphs representing any `if` and `for` statements""" if self.graph is None: # global loop - self.graph = PathGraph(name, name, node.lineno) + self.graph = PathGraph(name, name, node.lineno, node.col_offset) pathnode = PathNode(name) self._subgraph_parse(node, pathnode, extra_blocks) self.graphs["%s%s" % (self.classname, name)] = self.graph @@ -216,6 +222,8 @@ self.appendPathNode(name) self.dispatch_list(node.body) + visitAsyncWith = visitWith + class McCabeChecker(object): """McCabe cyclomatic complexity checker.""" @@ -223,20 +231,33 @@ 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: @@ -246,7 +267,7 @@ for graph in visitor.graphs.values(): if graph.complexity() > self.max_complexity: text = self._error_tmpl % (graph.entity, graph.complexity()) - yield graph.lineno, 0, text, type(self) + yield graph.lineno, graph.column, text, type(self) def get_code_complexity(code, threshold=7, filename='stdin'): @@ -275,6 +296,23 @@ return get_code_complexity(code, threshold, filename=module_path) +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:] @@ -287,8 +325,7 @@ 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) @@ -296,7 +333,8 @@ if options.dot: print('graph {') for graph in visitor.graphs.values(): - if not options.threshold or graph.complexity() >= options.threshold: + if (not options.threshold or + graph.complexity() >= options.threshold): graph.to_dot() print('}') else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/setup.cfg new/mccabe-0.6.1/setup.cfg --- old/mccabe-0.3/setup.cfg 2014-12-17 02:23:10.000000000 +0100 +++ new/mccabe-0.6.1/setup.cfg 2017-01-26 23:13:05.000000000 +0100 @@ -1,8 +1,10 @@ [wheel] universal = 1 +[aliases] +test = pytest + [egg_info] -tag_svn_revision = 0 -tag_date = 0 tag_build = +tag_date = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/setup.py new/mccabe-0.6.1/setup.py --- old/mccabe-0.3/setup.py 2014-04-25 23:21:22.000000000 +0200 +++ new/mccabe-0.6.1/setup.py 2017-01-23 13:24:16.000000000 +0100 @@ -27,27 +27,33 @@ keywords='flake8 mccabe', author='Tarek Ziade', author_email='[email protected]', - maintainer='Florent Xicluna', - maintainer_email='[email protected]', - url='https://github.com/flintwork/mccabe', + maintainer='Ian Cordasco', + maintainer_email='[email protected]', + url='https://github.com/pycqa/mccabe', license='Expat license', py_modules=['mccabe'], zip_safe=False, - test_suite='test_mccabe', + 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', + 'Programming Language :: Python :: 3.6', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Quality Assurance', ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mccabe-0.3/test_mccabe.py new/mccabe-0.6.1/test_mccabe.py --- old/mccabe-0.3/test_mccabe.py 1970-01-01 01:00:00.000000000 +0100 +++ new/mccabe-0.6.1/test_mccabe.py 2017-01-23 13:24:16.000000000 +0100 @@ -0,0 +1,234 @@ +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' + + +expr_as_statement = '''\ +def f(): + """docstring""" +''' + + +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 +""" + +annotated_assign = """\ +def f(): + x: Any = None +""" + + +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_expr_as_statement(self): + complexity = get_complexity_number(expr_as_statement, self.strio) + self.assertEqual(complexity, 1) + + 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) + + @pytest.mark.skipif( + sys.version_info < (3, 6), + reason="Annotated assignments are only valid on Python 3.6+", + ) + def test_annotated_assignment(self): + complexity = get_complexity_number(annotated_assign, self.strio) + self.assertEqual(complexity, 1) + + +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()
