Skip to content

Commit f655c07

Browse files
committed
refactor to read the policy from a file
1 parent a2d6511 commit f655c07

File tree

1 file changed

+92
-33
lines changed

1 file changed

+92
-33
lines changed

minimum_versions.py

Lines changed: 92 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
from dataclasses import dataclass, field
77

8+
import jsonschema
89
import rich_click as click
910
import yaml
1011
from dateutil.relativedelta import relativedelta
@@ -18,27 +19,56 @@
1819

1920
click.rich_click.SHOW_ARGUMENTS = True
2021

21-
channels = ["conda-forge"]
22-
platforms = ["noarch", "linux-64"]
23-
ignored_packages = [
24-
"coveralls",
25-
"hypothesis",
26-
"pip",
27-
"pytest",
28-
"pytest-cov",
29-
"pytest-env",
30-
"pytest-mypy-plugins",
31-
"pytest-timeout",
32-
"pytest-xdist",
33-
]
22+
23+
schema = {
24+
"type": "object",
25+
"properties": {
26+
"exclude": {"type": "array", "items": {"type": "string"}},
27+
"channels": {"type": "array", "items": {"type": "string"}},
28+
"platforms": {"type": "array", "items": {"type": "string"}},
29+
"overrides": {
30+
"type": "object",
31+
"patternProperties": {
32+
"^[a-z][-a-z_]*": {"type": "string", "format": "date"}
33+
},
34+
"additionalProperties": False,
35+
},
36+
"policy": {
37+
"type": "object",
38+
"properties": {
39+
"packages": {
40+
"type": "object",
41+
"patternProperties": {
42+
"^[a-z][-a-z_]*$": {"type": "integer", "minimum": 1}
43+
},
44+
"additionalProperties": False,
45+
},
46+
"default": {"type": "integer", "minimum": 1},
47+
"ignored_violations": {
48+
"type": "array",
49+
"items": {"type": "string", "pattern": "^[a-z][-a-z_]*$"},
50+
},
51+
},
52+
"required": ["packages", "default", "ignored_violations"],
53+
},
54+
},
55+
"required": ["exclude", "channels", "platforms", "overrides"],
56+
}
3457

3558

3659
@dataclass
3760
class Policy:
3861
package_months: dict
3962
default_months: int
63+
64+
channels: list[str] = field(default_factory=list)
65+
platforms: list[str] = field(default_factory=list)
66+
4067
overrides: dict[str, Version] = field(default_factory=dict)
4168

69+
ignored_violations: list[str] = field(default_factory=list)
70+
exclude: list[str] = field(default_factory=list)
71+
4272
def minimum_version(self, today, package_name, releases):
4373
if (override := self.overrides.get(package_name)) is not None:
4474
return find_release(releases, version=override)
@@ -117,6 +147,28 @@ def parse_environment(text):
117147
return specs, warnings
118148

119149

150+
def parse_policy(file):
151+
policy = yaml.safe_load(file)
152+
try:
153+
jsonschema.validate(instance=policy, schema=schema)
154+
except jsonschema.ValidationError as e:
155+
raise jsonschema.ValidationError(
156+
f"Invalid policy definition: {str(e)}"
157+
) from None
158+
159+
package_policy = policy["policy"]
160+
161+
return Policy(
162+
channels=policy["channels"],
163+
platforms=policy["platforms"],
164+
exclude=policy["exclude"],
165+
package_months=package_policy["packages"],
166+
default_months=package_policy["default"],
167+
ignored_violations=package_policy["ignored_violations"],
168+
overrides=policy["overrides"],
169+
)
170+
171+
120172
def is_preview(version):
121173
candidates = {"rc", "b", "a"}
122174

@@ -175,11 +227,15 @@ def lookup_spec_release(spec, releases):
175227
return releases[spec.name][version]
176228

177229

178-
def compare_versions(environments, policy_versions):
230+
def compare_versions(environments, policy_versions, ignored_violations):
179231
status = {}
180232
for env, specs in environments.items():
181233
env_status = any(
182-
spec.version > policy_versions[spec.name].version for spec in specs
234+
(
235+
spec.name not in ignored_violations
236+
and spec.version > policy_versions[spec.name].version
237+
)
238+
for spec in specs
183239
)
184240
status[env] = env_status
185241
return status
@@ -194,7 +250,7 @@ def version_comparison_symbol(required, policy):
194250
return "="
195251

196252

197-
def format_bump_table(specs, policy_versions, releases, warnings):
253+
def format_bump_table(specs, policy_versions, releases, warnings, ignored_violations):
198254
table = Table(
199255
Column("Package", width=20),
200256
Column("Required", width=8),
@@ -221,7 +277,10 @@ def format_bump_table(specs, policy_versions, releases, warnings):
221277
required_date = lookup_spec_release(spec, releases).timestamp
222278

223279
status = version_comparison_symbol(required_version, policy_version)
224-
style = styles[status]
280+
if status == ">" and spec.name in ignored_violations:
281+
style = warning_style
282+
else:
283+
style = styles[status]
225284

226285
table.add_row(
227286
spec.name,
@@ -261,9 +320,12 @@ def format_bump_table(specs, policy_versions, releases, warnings):
261320
type=click.Path(exists=True, readable=True, path_type=pathlib.Path),
262321
nargs=-1,
263322
)
264-
def main(environment_paths):
323+
@click.option("--policy", "policy_file", type=click.File(mode="r"))
324+
def main(policy_file, environment_paths):
265325
console = Console()
266326

327+
policy = parse_policy(policy_file)
328+
267329
parsed_environments = {
268330
path.stem: parse_environment(path.read_text()) for path in environment_paths
269331
}
@@ -272,27 +334,18 @@ def main(environment_paths):
272334
env: dict(warnings_) for env, (_, warnings_) in parsed_environments.items()
273335
}
274336
environments = {
275-
env: [spec for spec in specs if spec.name not in ignored_packages]
337+
env: [spec for spec in specs if spec.name not in policy.exclude]
276338
for env, (specs, _) in parsed_environments.items()
277339
}
278340

279341
all_packages = list(
280342
dict.fromkeys(spec.name for spec in concat(environments.values()))
281343
)
282344

283-
policy_months = {
284-
"python": 30,
285-
"numpy": 18,
286-
}
287-
policy_months_default = 12
288-
overrides = {}
289-
290-
policy = Policy(
291-
policy_months, default_months=policy_months_default, overrides=overrides
292-
)
293-
294345
gateway = Gateway()
295-
query = gateway.query(channels, platforms, all_packages, recursive=False)
346+
query = gateway.query(
347+
policy.channels, policy.platforms, all_packages, recursive=False
348+
)
296349
records = asyncio.run(query)
297350

298351
today = datetime.date.today()
@@ -307,13 +360,19 @@ def main(environment_paths):
307360
package_releases,
308361
curry(find_policy_versions, policy, today),
309362
)
310-
status = compare_versions(environments, policy_versions)
363+
status = compare_versions(environments, policy_versions, policy.ignored_violations)
311364

312365
release_lookup = {
313366
n: {r.version: r for r in releases} for n, releases in package_releases.items()
314367
}
315368
grids = {
316-
env: format_bump_table(specs, policy_versions, release_lookup, warnings[env])
369+
env: format_bump_table(
370+
specs,
371+
policy_versions,
372+
release_lookup,
373+
warnings[env],
374+
policy.ignored_violations,
375+
)
317376
for env, specs in environments.items()
318377
}
319378
root_grid = Table.grid()

0 commit comments

Comments
 (0)