-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
Halstead operator.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
Measures all of the halstead metrics (volume, vocab, difficulty)
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
import ast
-- -- --- --- --- --- --- --- ------- ------- ------- |
import collections
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
import radon.cli.harvest as harvesters
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.cli import Config
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.metrics import Halstead, HalsteadReport, halstead_visitor_report
-- -- --- --- --- --- --- --- ------- ------- ------- |
from radon.visitors import HalsteadVisitor
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily import logger
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.lang import _
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators import BaseOperator, Metric, MetricType
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
NumberedHalsteadReport = collections.namedtuple(
-- -- --- --- --- --- --- --- ------- ------- ------- |
"NumberedHalsteadReport",
-- -- --- --- --- --- --- --- ------- ------- ------- |
HalsteadReport._fields + ("lineno", "endline"),
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
class NumberedHalsteadVisitor(HalsteadVisitor):
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(self, context=None, lineno=None, endline=None, classname=None):
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
super().__init__(context)
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.lineno = lineno
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.endline = endline
03 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.class_name = classname
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
def visit_FunctionDef(self, node):
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
if self.class_name:
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
node.name = f"{self.class_name}.{node.name}"
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
super().visit_FunctionDef(node)
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
self.function_visitors[-1].lineno = node.lineno
03 02 001 001 002 002 002 004 0004.00 0004.00 0001.00 |
self.function_visitors[-1].endline = node.end_lineno
03 -- --- --- --- --- --- --- ------- ------- ------- |
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def visit_ClassDef(self, node):
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.class_name = node.name
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
for child in node.body:
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
visitor = NumberedHalsteadVisitor(classname=self.class_name)
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
visitor.visit(child)
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.function_visitors.extend(visitor.function_visitors)
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.class_name = None
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def number_report(visitor):
-- 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
return NumberedHalsteadReport(
-- 01 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
*(halstead_visitor_report(visitor) + (visitor.lineno, visitor.endline))
-- -- 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
03 -- --- --- --- --- --- --- ------- ------- ------- |
class NumberedHCHarvester(harvesters.HCHarvester):
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def gobble(self, fobj):
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""Analyze the content of the file object."""
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
code = fobj.read()
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
visitor = NumberedHalsteadVisitor.from_ast(ast.parse(code))
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
total = number_report(visitor)
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
functions = [(v.context, number_report(v)) for v in visitor.function_visitors]
03 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return Halstead(total, functions)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
04 -- --- --- --- --- --- --- ------- ------- ------- |
class HalsteadOperator(BaseOperator):
04 -- --- --- --- --- --- --- ------- ------- ------- |
"""Halstead Operator."""
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 -- --- --- --- --- --- --- ------- ------- ------- |
name = "halstead"
04 -- --- --- --- --- --- --- ------- ------- ------- |
defaults = {
04 -- --- --- --- --- --- --- ------- ------- ------- |
"exclude": None,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"ignore": None,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"min": "A",
04 -- --- --- --- --- --- --- ------- ------- ------- |
"max": "C",
04 -- --- --- --- --- --- --- ------- ------- ------- |
"multi": True,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"show": False,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"sort": False,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"by_function": True,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"include_ipynb": True,
04 -- --- --- --- --- --- --- ------- ------- ------- |
"ipynb_cells": True,
04 -- --- --- --- --- --- --- ------- ------- ------- |
}
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 -- --- --- --- --- --- --- ------- ------- ------- |
metrics = (
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("h1", _("Unique Operands"), int, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("h2", _("Unique Operators"), int, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("N1", _("Number of Operands"), int, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("N2", _("Number of Operators"), int, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric(
04 -- --- --- --- --- --- --- ------- ------- ------- |
"vocabulary", _("Unique vocabulary (h1 + h2)"), int, MetricType.AimLow, sum
04 -- --- --- --- --- --- --- ------- ------- ------- |
),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("length", _("Length of application"), int, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("volume", _("Code volume"), float, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("difficulty", _("Difficulty"), float, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
Metric("effort", _("Effort"), float, MetricType.AimLow, sum),
04 -- --- --- --- --- --- --- ------- ------- ------- |
)
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 -- --- --- --- --- --- --- ------- ------- ------- |
default_metric_index = 0 # MI
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(self, config, targets):
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
Instantiate a new HC operator.
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param config: The wily configuration.
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type config: :class:`WilyConfig`
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
# TODO : Import config from wily.cfg
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
logger.debug(f"Using {targets} with {self.defaults} for HC metrics")
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.harvester = NumberedHCHarvester(targets, config=Config(**self.defaults))
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
def run(self, module, options):
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
"""
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
Run the operator.
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:param module: The target module path.
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:type module: ``str``
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:param options: Any runtime options.
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:type options: ``dict``
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:return: The operator results.
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
:rtype: ``dict``
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
"""
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
logger.debug("Running halstead harvester")
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
results = {}
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
for filename, details in dict(self.harvester.results).items():
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
results[filename] = {"detailed": {}, "total": {}}
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
for instance in details:
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
if isinstance(instance, list):
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
for item in instance:
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
function, report = item
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
results[filename]["detailed"][function] = self._report_to_dict(
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
report
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
)
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
else:
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
if isinstance(instance, str) and instance == "error":
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
logger.debug(
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
f"Failed to run Halstead harvester on {filename} : {details['error']}"
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
)
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
continue
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
results[filename]["total"] = self._report_to_dict(instance)
04 07 002 004 002 004 006 006 0015.51 0015.51 0001.00 |
return results
04 -- --- --- --- --- --- --- ------- ------- ------- |
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def _report_to_dict(self, report):
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return {
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"h1": report.h1,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"h2": report.h2,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"N1": report.N1,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"N2": report.N2,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"vocabulary": report.vocabulary,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"volume": report.volume,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"length": report.length,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"effort": report.effort,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"difficulty": report.difficulty,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"lineno": report.lineno,
04 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"endline": report.endline,
-- -- 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
}