-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Models and types for "operators" the basic measure of a module that measures code."""
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from collections import namedtuple
-- -- --- --- --- --- --- --- ------- ------- ------- |
from enum import Enum
-- -- --- --- --- --- --- --- ------- ------- ------- |
from functools import lru_cache
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
01 -- --- --- --- --- --- --- ------- ------- ------- |
class MetricType(Enum):
01 -- --- --- --- --- --- --- ------- ------- ------- |
"""Type of metric, used in trends."""
01 -- --- --- --- --- --- --- ------- ------- ------- |
01 -- --- --- --- --- --- --- ------- ------- ------- |
AimLow = 1 # Low is good, high is bad
01 -- --- --- --- --- --- --- ------- ------- ------- |
AimHigh = 2 # High is good, low is bad
01 -- --- --- --- --- --- --- ------- ------- ------- |
Informational = 3 # Doesn't matter
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
Metric = namedtuple("Metric", "name description type measure aggregate")
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
GOOD_COLORS = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.AimHigh: 32,
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.AimLow: 31,
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.Informational: 33,
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
BAD_COLORS = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.AimHigh: 31,
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.AimLow: 32,
-- -- --- --- --- --- --- --- ------- ------- ------- |
MetricType.Informational: 33,
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
01 -- --- --- --- --- --- --- ------- ------- ------- |
class OperatorLevel(Enum):
01 -- --- --- --- --- --- --- ------- ------- ------- |
"""Level of operator."""
01 -- --- --- --- --- --- --- ------- ------- ------- |
01 -- --- --- --- --- --- --- ------- ------- ------- |
File = 1
01 -- --- --- --- --- --- --- ------- ------- ------- |
Object = 2
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
class BaseOperator(object):
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Abstract Operator Class."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Name of the operator."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
name = "abstract"
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Default settings."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
defaults = {}
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Available metrics as a list of tuple ("name"<str>, "description"<str>, "type"<type>, "metric_type"<MetricType>)."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
metrics = ()
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Which metric is the default to display in the report command."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
default_metric_index = None
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Level at which the operator goes to."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
level = OperatorLevel.File
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def run(self, module, options):
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
Run the operator.
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param module: The target module path.
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type module: ``str``
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param options: Any runtime options.
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type options: ``dict``
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:return: The operator results.
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:rtype: ``dict``
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
raise NotImplementedError
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.cyclomatic import CyclomaticComplexityOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.maintainability import MaintainabilityIndexOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.raw import RawMetricsOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.halstead import HalsteadOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Type for an operator."""
-- -- --- --- --- --- --- --- ------- ------- ------- |
Operator = namedtuple("Operator", "name cls description level")
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_CYCLOMATIC = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="cyclomatic",
-- -- --- --- --- --- --- --- ------- ------- ------- |
cls=CyclomaticComplexityOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description="Cyclomatic Complexity of modules",
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.Object,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_RAW = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="raw",
-- -- --- --- --- --- --- --- ------- ------- ------- |
cls=RawMetricsOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description="Raw Python statistics",
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.File,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_MAINTAINABILITY = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="maintainability",
-- -- --- --- --- --- --- --- ------- ------- ------- |
cls=MaintainabilityIndexOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description="Maintainability index (lines of code and branching)",
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.File,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_HALSTEAD = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="halstead",
-- -- --- --- --- --- --- --- ------- ------- ------- |
cls=HalsteadOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description="Halstead metrics",
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.Object,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Dictionary of all operators"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
ALL_OPERATORS = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator.name: operator
-- -- --- --- --- --- --- --- ------- ------- ------- |
for operator in {
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_CYCLOMATIC,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_MAINTAINABILITY,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_RAW,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_HALSTEAD,
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Set of all metrics"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
ALL_METRICS = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
(operator, metric)
-- -- --- --- --- --- --- --- ------- ------- ------- |
for operator in ALL_OPERATORS.values()
-- -- --- --- --- --- --- --- ------- ------- ------- |
for metric in operator.cls.metrics
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
@lru_cache(maxsize=128)
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def resolve_operator(name):
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"""
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
Get the :namedtuple:`wily.operators.Operator` for a given name.
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:param name: The name of the operator
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:return: The operator type
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"""
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
if name.lower() in ALL_OPERATORS:
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
return ALL_OPERATORS[name.lower()]
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
else:
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
raise ValueError(f"Operator {name} not recognised.")
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def resolve_operators(operators):
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
Resolve a list of operator names to their corresponding types.
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param operators: The list of operators
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type operators: iterable or ``str``
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:rtype: ``list`` of :class:`Operator`
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
-- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return [resolve_operator(operator) for operator in iter(operators)]
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
@lru_cache(maxsize=128)
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def resolve_metric(metric):
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
Resolve metric key to a given target.
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:param metric: the metric name.
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:type metric: ``str``
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
:rtype: :class:`Metric`
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return resolve_metric_as_tuple(metric)[1]
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
@lru_cache(maxsize=128)
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
def resolve_metric_as_tuple(metric):
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
"""
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
Resolve metric key to a given target.
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
:param metric: the metric name.
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
:type metric: ``str``
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
:rtype: :class:`Metric`
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
"""
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
if "." in metric:
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
_, metric = metric.split(".")
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
r = [(operator, match) for operator, match in ALL_METRICS if match[0] == metric]
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
if not r or len(r) == 0:
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
raise ValueError(f"Metric {metric} not recognised.")
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
else:
-- 06 004 008 005 009 012 014 0050.19 0112.93 0002.25 |
return r[0]
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def get_metric(revision, operator, path, key):
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"""
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
Get a metric from the cache.
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:param revision: The revision data.
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:type revision: ``dict``
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:param operator: The operator name.
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:type operator: ``str``
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:param path: The path to the file/function
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:type path: ``str``
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:param key: The key of the data
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:type key: ``str``
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:return: Data from the cache
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
:rtype: ``dict``
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
"""
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
if ":" in path:
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
part, entry = path.split(":")
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
val = revision[operator][part]["detailed"][entry][key]
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
else:
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
val = revision[operator][path]["total"][key]
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
return val