-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Models and types for "operators" the basic measure of a module that measures code."""
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from enum import Enum
-- -- --- --- --- --- --- --- ------- ------- ------- |
from functools import lru_cache
-- -- --- --- --- --- --- --- ------- ------- ------- |
from typing import (
-- -- --- --- --- --- --- --- ------- ------- ------- |
Any,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Callable,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Dict,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Generic,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Iterable,
-- -- --- --- --- --- --- --- ------- ------- ------- |
List,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Optional,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Set,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Tuple,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Type,
-- -- --- --- --- --- --- --- ------- ------- ------- |
TypeVar,
-- -- --- --- --- --- --- --- ------- ------- ------- |
Union,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.config.types import WilyConfig
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.lang import _
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
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
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
TValue = TypeVar("TValue", str, int, float)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
class Metric(Generic[TValue]):
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Represents a metric."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
name: str
02 -- --- --- --- --- --- --- ------- ------- ------- |
description: str
02 -- --- --- --- --- --- --- ------- ------- ------- |
metric_type: TValue
02 -- --- --- --- --- --- --- ------- ------- ------- |
measure: MetricType
02 -- --- --- --- --- --- --- ------- ------- ------- |
aggregate: Callable[[Iterable[TValue]], TValue]
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
name: str,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
description: str,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
metric_type: TValue,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
measure: MetricType,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
aggregate: Callable[[Iterable[TValue]], TValue],
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 |
"""Initialise the metric."""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.name = name
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.description = description
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.metric_type = metric_type
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.measure = measure
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.aggregate = 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:
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Abstract Operator Class."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Name of the operator."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
name: str = "abstract"
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Default settings."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
defaults: Dict[str, Any] = {}
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Available metrics as a list of tuple ("name"<str>, "description"<str>, "type"<type>, "metric_type"<MetricType>)."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
metrics: Tuple[Metric, ...] = ()
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Which metric is the default to display in the report command."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
default_metric_index: Optional[int] = None
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Level at which the operator goes to."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
level: OperatorLevel = OperatorLevel.File
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(self, *args, **kwargs):
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""Initialise the operator."""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
...
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def run(self, module: str, options: Dict[str, Any]) -> Dict[Any, Any]:
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.halstead import HalsteadOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.maintainability import MaintainabilityIndexOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators.raw import RawMetricsOperator
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Type for an operator."""
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
T = TypeVar("T", bound=BaseOperator)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
class Operator(Generic[T]):
02 -- --- --- --- --- --- --- ------- ------- ------- |
"""Operator holder."""
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 -- --- --- --- --- --- --- ------- ------- ------- |
name: str
02 -- --- --- --- --- --- --- ------- ------- ------- |
operator_cls: Type[T]
02 -- --- --- --- --- --- --- ------- ------- ------- |
description: str
02 -- --- --- --- --- --- --- ------- ------- ------- |
level: OperatorLevel
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __init__(
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
name: str,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
operator_cls: Type[T],
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
description: str,
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
level: OperatorLevel = OperatorLevel.File,
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 |
"""Initialise the operator."""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.name = name
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.operator_cls = operator_cls
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.description = description
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
self.level = level
02 -- --- --- --- --- --- --- ------- ------- ------- |
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def __call__(self, config: "WilyConfig") -> T:
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""Initialise the operator."""
02 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return self.operator_cls(config)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_CYCLOMATIC = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="cyclomatic",
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator_cls=CyclomaticComplexityOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description=_("Cyclomatic Complexity of modules"),
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.Object,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_RAW = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="raw",
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator_cls=RawMetricsOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description=_("Raw Python statistics"),
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.File,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_MAINTAINABILITY = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="maintainability",
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator_cls=MaintainabilityIndexOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description=_("Maintainability index (lines of code and branching)"),
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.File,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_HALSTEAD = Operator(
-- -- --- --- --- --- --- --- ------- ------- ------- |
name="halstead",
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator_cls=HalsteadOperator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
description=_("Halstead metrics"),
-- -- --- --- --- --- --- --- ------- ------- ------- |
level=OperatorLevel.Object,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Dictionary of all operators"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
ALL_OPERATORS: Dict[str, Operator] = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
operator.name: operator
-- -- --- --- --- --- --- --- ------- ------- ------- |
for operator in (
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_CYCLOMATIC,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_MAINTAINABILITY,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_RAW,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OPERATOR_HALSTEAD,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""Set of all metrics"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
ALL_METRICS: Set[Tuple[Operator, Metric[Any]]] = {
-- -- --- --- --- --- --- --- ------- ------- ------- |
(operator, metric)
-- -- --- --- --- --- --- --- ------- ------- ------- |
for operator in ALL_OPERATORS.values()
-- -- --- --- --- --- --- --- ------- ------- ------- |
for metric in operator.operator_cls.metrics
-- -- --- --- --- --- --- --- ------- ------- ------- |
}
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
@lru_cache(maxsize=128)
-- 02 001 002 001 002 003 003 0004.75 0002.38 0000.50 |
def resolve_operator(name: str) -> Operator:
-- 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: Iterable[Union[Operator, str]]) -> List[Operator]:
-- 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 |
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: str) -> Metric:
-- 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 |
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: str) -> Tuple[Operator, Metric]:
-- 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 |
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.name == 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: Dict[Any, Any], operator: str, path: str, key: str) -> Any:
-- 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 |
:param operator: The operator name.
-- 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 |
:param key: The key of the data
-- 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 |
"""
-- 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