Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions minimum_versions/environments/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@ class Spec:

def compare_versions(environments, policy_versions, ignored_violations):
status = {}
warnings = {}
for env, specs in environments.items():
env_status = any(
(
spec.name not in ignored_violations
and (
spec.version is None
or spec.version > policy_versions[spec.name].version
)
)
violations = {
spec.name: spec.version is None
or spec.version > policy_versions[spec.name].version
for spec in specs
}
status[env] = any(
value
for name, value in violations.items()
if name not in ignored_violations
)
status[env] = env_status
return status
warnings[env] = {
name: ["violation unnecessarily ignored"]
for name, value in violations.items()
if not value and name in ignored_violations
}

return status, warnings
16 changes: 11 additions & 5 deletions minimum_versions/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,18 @@ def format_bump_table(specs, policy_versions, releases, warnings, ignored_violat
warning_table.add_column("Package")
warning_table.add_column("Warning")

formatters = {
"indexed": lambda index, message: f"{index: 2d}. {message}",
"bare": lambda index, message: f" {message}",
}

for package, messages in warnings.items():
if not messages:
continue
warning_table.add_row(package, messages[0], style=warning_style)
for message in messages[1:]:
warning_table.add_row("", message, style=warning_style)
formatter = formatters["indexed" if len(messages) > 1 else "bare"]
for index, message in enumerate(messages, start=1):
key = "" if index > 1 else package
warning_table.add_row(
key, formatter(index, message), style=warning_style
)

grid.add_row("Warnings", warning_table)

Expand Down
20 changes: 18 additions & 2 deletions minimum_versions/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from tlz.dicttoolz import merge_with
from tlz.itertoolz import concat, unique

from minimum_versions.environments import compare_versions, parse_environment
Expand Down Expand Up @@ -35,6 +36,16 @@ def convert(
return super().convert(value, param, ctx)


def merge_warnings(*warnings):
def merge_lists(v):
return list(concat(v))

def merge_env(values):
return merge_with(merge_lists, *values)

return merge_with(merge_env, *warnings)


@click.group()
def main():
pass
Expand All @@ -60,7 +71,7 @@ def validate(today, policy_file, manifest_path, environment_paths):
for path in environment_paths
}

warnings = {
spec_warnings = {
env: {n: w for n, w in warnings_ if n not in policy.exclude}
for env, (_, warnings_) in parsed_environments.items()
}
Expand All @@ -84,11 +95,16 @@ def validate(today, policy_file, manifest_path, environment_paths):

policy_versions = find_policy_versions(policy, today, package_releases)

status = compare_versions(environments, policy_versions, policy.ignored_violations)
status, violation_warnings = compare_versions(
environments, policy_versions, policy.ignored_violations
)

warnings = merge_warnings(spec_warnings, violation_warnings)

release_lookup = {
n: {r.version: r for r in releases} for n, releases in package_releases.items()
}

grids = {
env: format_bump_table(
specs,
Expand Down
18 changes: 15 additions & 3 deletions minimum_versions/tests/test_environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_parse_environment(specifier, manifest_path, key, monkeypatch):


@pytest.mark.parametrize(
["envs", "ignored_violations", "expected"],
["envs", "ignored_violations", "expected", "expected_warnings"],
(
pytest.param(
{
Expand All @@ -46,6 +46,7 @@ def test_parse_environment(specifier, manifest_path, key, monkeypatch):
},
["d"],
{"env1": False},
{"env1": {}},
id="single-violation-ignored",
),
pytest.param(
Expand All @@ -55,34 +56,45 @@ def test_parse_environment(specifier, manifest_path, key, monkeypatch):
},
[],
{"env1": True, "env2": False},
{"env1": {}, "env2": {}},
id="multiple-split-not ignored",
),
pytest.param(
{"env1": [Spec("d", None)]},
[],
{"env1": True},
{"env1": {}},
id="single-none-not ignored",
),
pytest.param(
{"env1": [Spec("d", None)]},
["d"],
{"env1": False},
{"env1": {}},
id="single-none-ignored",
),
pytest.param(
{"env1": [Spec("a", Version("1.2"))]},
["a"],
{"env1": False},
{"env1": {"a": ["violation unnecessarily ignored"]}},
id="single-none-unnecessary_ignore",
),
),
)
def test_compare_versions(envs, ignored_violations, expected):
def test_compare_versions(envs, ignored_violations, expected, expected_warnings):
policy_versions = {
"a": FakeRecord(version=Version("1.2")),
"b": FakeRecord(version=Version("3.1")),
"c": FakeRecord(version=Version("2025.1")),
"d": FakeRecord(version=Version("0.5")),
}

actual = environments.spec.compare_versions(
actual, actual_warnings = environments.spec.compare_versions(
envs, policy_versions, ignored_violations
)
assert actual == expected
assert actual_warnings == expected_warnings


class TestCondaEnvironment:
Expand Down