src\wily\operators\cyclomatic.py
-- -- --- --- --- --- --- --- ------- ------- ------- | """
-- -- --- --- --- --- --- --- ------- ------- ------- | 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 | }