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