-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
Diff command.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
Compares metrics between uncommitted files and indexed files.
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
import multiprocessing
-- -- --- --- --- --- --- --- ------- ------- ------- |
import os
-- -- --- --- --- --- --- --- ------- ------- ------- |
from pathlib import Path
-- -- --- --- --- --- --- --- ------- ------- ------- |
from sys import exit
-- -- --- --- --- --- --- --- ------- ------- ------- |
from typing import List, Optional
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
import radon.cli.harvest
-- -- --- --- --- --- --- --- ------- ------- ------- |
import tabulate
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily import format_date, format_revision, logger
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.archivers import resolve_archiver
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.commands.build import run_operator
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.config import DEFAULT_PATH
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.config.types import WilyConfig
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.helper import get_maxcolwidth, get_style
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators import (
-- -- --- --- --- --- --- --- ------- ------- ------- |
BAD_COLORS,
-- -- --- --- --- --- --- --- ------- ------- ------- |
GOOD_COLORS,
-- -- --- --- --- --- --- --- ------- ------- ------- |
OperatorLevel,
-- -- --- --- --- --- --- --- ------- ------- ------- |
get_metric,
-- -- --- --- --- --- --- --- ------- ------- ------- |
resolve_metric,
-- -- --- --- --- --- --- --- ------- ------- ------- |
resolve_operator,
-- -- --- --- --- --- --- --- ------- ------- ------- |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.state import State
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
def diff(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
config: WilyConfig,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
files: List[str],
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics: List[str],
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
changes_only: bool = True,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
detail: bool = True,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
revision: Optional[str] = None,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
wrap: bool = False,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
) -> None:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
"""
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
Show the differences in metrics for each of the files.
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param config: The wily configuration
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param files: The files to compare.
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param metrics: The metrics to measure.
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param changes_only: Only include changes files in output.
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param detail: Show details (function-level)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param revision: Compare with specific revision
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
:param wrap: Wrap output
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
"""
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
config.targets = files
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
files = list(files)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
state = State(config)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# Resolve target paths when the cli has specified --path
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if config.path != DEFAULT_PATH:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
targets = [str(Path(config.path) / Path(file)) for file in files]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
targets = files
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# Expand directories to paths
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
files = [
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
os.path.relpath(fn, config.path)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for fn in radon.cli.harvest.iter_filenames(targets)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(f"Targeting - {files}")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if not revision:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
target_revision = state.index[state.default_archiver].last_revision
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
rev = (
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
resolve_archiver(state.default_archiver).archiver_cls(config).find(revision)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(f"Resolved {revision} to {rev.key} ({rev.message})")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
try:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
target_revision = state.index[state.default_archiver][rev.key]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
except KeyError:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.error(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
f"Revision {revision} is not in the cache, make sure you have run wily build."
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
exit(1)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.info(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
f"Comparing current with {format_revision(target_revision.revision.key)} by {target_revision.revision.author_name} on {format_date(target_revision.revision.date)}."
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# Convert the list of metrics to a list of metric instances
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
operators = {resolve_operator(metric.split(".")[0]) for metric in metrics}
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
resolved_metrics = [
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
(metric.split(".")[0], resolve_metric(metric)) for metric in metrics
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
results = []
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# Build a set of operators
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
with multiprocessing.Pool(processes=len(operators)) as pool:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
operator_exec_out = pool.starmap(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
run_operator, [(operator, None, config, targets) for operator in operators]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
data = {}
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for operator_name, result in operator_exec_out:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
data[operator_name] = result
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# Write a summary table
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
extra = []
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for operator, metric in resolved_metrics:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if detail and resolve_operator(operator).level == OperatorLevel.Object:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for file in files:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
try:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
extra.extend(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
[
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
f"{file}:{k}"
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for k in data[operator][file]["detailed"].keys()
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if k != metric.name
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
and isinstance(data[operator][file]["detailed"][k], dict)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
except KeyError:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(f"File {file} not in cache")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug("Cache follows -- ")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(data[operator])
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
files.extend(extra)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(files)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for file in files:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data = []
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
has_changes = False
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
for operator, metric in resolved_metrics:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
try:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
current = target_revision.get(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
config, state.default_archiver, operator, file, metric.name
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
except KeyError:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
current = "-"
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
try:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
new = get_metric(data, operator, file, metric.name)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
except KeyError:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
new = "-"
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if new != current:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
has_changes = True
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if metric.metric_type in (int, float) and new != "-" and current != "-":
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if current > new: # type: ignore
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data.append(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
"{0:n} -> \u001b[{2}m{1:n}\u001b[0m".format(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
current, new, BAD_COLORS[metric.measure]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
elif current < new: # type: ignore
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data.append(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
"{0:n} -> \u001b[{2}m{1:n}\u001b[0m".format(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
current, new, GOOD_COLORS[metric.measure]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data.append(f"{current:n} -> {new:n}")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if current == "-" and new == "-":
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data.append("-")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
metrics_data.append(f"{current} -> {new}")
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if has_changes or not changes_only:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
results.append((file, *metrics_data))
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
else:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
logger.debug(metrics_data)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
descriptions = [metric.description for _, metric in resolved_metrics]
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
headers = ("File", *descriptions)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
if len(results) > 0:
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
maxcolwidth = get_maxcolwidth(headers, wrap)
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
style = get_style()
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
print(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
# But it still makes more sense to show the newest at the top, so reverse again
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
tabulate.tabulate(
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
headers=headers,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
tabular_data=results,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
tablefmt=style,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
maxcolwidths=maxcolwidth,
-- 34 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
maxheadercolwidths=maxcolwidth,
-- -- 009 028 020 039 037 059 0307.36 1926.47 0006.27 |
)
-- -- --- --- --- --- --- --- ------- ------- ------- |
)