-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
Cyclomatic complexity metric for each function/method.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
Provided by the radon library.
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
import statistics
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
import radon.cli.harvest as harvesters
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.cli import Config
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.complexity import SCORE
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.visitors import Class, Function
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily import logger
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.lang import _
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators import BaseOperator, Metric, MetricType
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
class CyclomaticComplexityOperator(BaseOperator):
03 -- --- --- --- --- --- --- ------- ------- ------- |
"""Cyclomatic complexity operator."""
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
name = "cyclomatic"
03 -- --- --- --- --- --- --- ------- ------- ------- |
defaults = {
03 -- --- --- --- --- --- --- ------- ------- ------- |
"exclude": None,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"ignore": None,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"min": "A",
03 -- --- --- --- --- --- --- ------- ------- ------- |
"max": "F",
03 -- --- --- --- --- --- --- ------- ------- ------- |
"no_assert": True,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"show_closures": False,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"order": SCORE,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"include_ipynb": True,
03 -- --- --- --- --- --- --- ------- ------- ------- |
"ipynb_cells": True,
03 -- --- --- --- --- --- --- ------- ------- ------- |
}
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
metrics = (
03 -- --- --- --- --- --- --- ------- ------- ------- |
Metric(
03 -- --- --- --- --- --- --- ------- ------- ------- |
"complexity",
03 -- --- --- --- --- --- --- ------- ------- ------- |
_("Cyclomatic Complexity"),
03 -- --- --- --- --- --- --- ------- ------- ------- |
float,
03 -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.AimLow,
03 -- --- --- --- --- --- --- ------- ------- ------- |
statistics.mean,
03 -- --- --- --- --- --- --- ------- ------- ------- |
),
03 -- --- --- --- --- --- --- ------- ------- ------- |
)
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
default_metric_index = 0 # MI
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(self, config, targets):
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
Instantiate a new Cyclomatic Complexity operator.
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param config: The wily configuration.
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type config: :class:`WilyConfig`
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
# TODO: Import config for harvester from .wily.cfg
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
logger.debug(f"Using {targets} with {self.defaults} for CC metrics")
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.harvester = harvesters.CCHarvester(targets, config=Config(**self.defaults))
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
def run(self, module, options):
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
"""
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
Run the operator.
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:param module: The target module path.
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:type module: ``str``
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:param options: Any runtime options.
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:type options: ``dict``
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:return: The operator results.
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
:rtype: ``dict``
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
"""
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
logger.debug("Running CC harvester")
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
results = {}
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
for filename, details in dict(self.harvester.results).items():
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
results[filename] = {"detailed": {}, "total": {}}
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
total = 0 # running CC total
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
for instance in details:
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
if isinstance(instance, Class):
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
i = self._dict_from_class(instance)
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
elif isinstance(instance, Function):
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
i = self._dict_from_function(instance)
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
else:
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
if isinstance(instance, str) and instance == "error":
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
logger.debug(
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
f"Failed to run CC harvester on {filename} : {details['error']}"
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
)
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
continue
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
else:
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
logger.warning(
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
f"Unexpected result from Radon : {instance} of {type(instance)}. Please report on Github."
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
)
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
continue
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
results[filename]["detailed"][i["fullname"]] = i
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
del i["fullname"]
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
total += i["complexity"]
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
results[filename]["total"]["complexity"] = total
03 07 003 006 003 006 009 009 0028.53 0042.79 0001.50 |
return results
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
@staticmethod
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def _dict_from_function(l):
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
return {
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"name": l.name,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"is_method": l.is_method,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"classname": l.classname,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"closures": l.closures,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"complexity": l.complexity,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"fullname": l.fullname,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"loc": l.endline - l.lineno,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"lineno": l.lineno,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"endline": l.endline,
03 -- 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
}
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
@staticmethod
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def _dict_from_class(l):
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
return {
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"name": l.name,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"inner_classes": l.inner_classes,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"real_complexity": l.real_complexity,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"complexity": l.complexity,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"fullname": l.fullname,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"loc": l.endline - l.lineno,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"lineno": l.lineno,
03 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"endline": l.endline,
-- -- 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
}