Skip to content

Commit cf8e2f6

Browse files
committed
Register the optimization schema with register_strategy
Instead of hardcoding the schema, make it part of the registration of the strategy, this allows for people to add their own strategy without having to work around `OptimizationSchema` which is painful and brittle. The slight drawback from this is that we lose the documentation in the task schema about what is possible since it's now deferred to the strategies themselves, and that we now validate optimizations twice, first to check that it's a dict then to check that it matches the strategy. Fixes #368
1 parent 76511bd commit cf8e2f6

File tree

5 files changed

+28
-16
lines changed

5 files changed

+28
-16
lines changed

src/taskgraph/optimize/base.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@
2222
from taskgraph.taskgraph import TaskGraph
2323
from taskgraph.util.parameterization import resolve_task_references, resolve_timestamps
2424
from taskgraph.util.python_path import import_sibling_modules
25+
from taskgraph.util.schema import validate_schema
2526
from taskgraph.util.taskcluster import find_task_id_batched, status_task_batched
2627

2728
logger = logging.getLogger("optimization")
2829
registry = {}
2930

3031

31-
def register_strategy(name, args=(), kwargs=None):
32+
def register_strategy(name, args=(), kwargs=None, schema=None):
3233
kwargs = kwargs or {}
3334

3435
def wrap(cls):
3536
if name not in registry:
3637
registry[name] = cls(*args, **kwargs)
3738
if not hasattr(registry[name], "description"):
3839
registry[name].description = name
40+
registry[name].schema = schema
3941
return cls
4042

4143
return wrap
@@ -123,6 +125,13 @@ def optimizations(label):
123125
if task.optimization:
124126
opt_by, arg = list(task.optimization.items())[0]
125127
strategy = strategies[opt_by]
128+
schema = getattr(strategy, "schema", None)
129+
if schema:
130+
validate_schema(
131+
schema,
132+
arg,
133+
f"In task `{label}` optimization `{opt_by}`:",
134+
)
126135
if hasattr(strategy, "description"):
127136
opt_by += f" ({strategy.description})"
128137
return (opt_by, strategy, arg)

src/taskgraph/optimize/strategies.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
from taskgraph.optimize.base import OptimizationStrategy, register_strategy
77
from taskgraph.util.path import match as match_path
8+
from taskgraph.util.schema import Schema
89
from taskgraph.util.taskcluster import find_task_id, status_task
910

1011
logger = logging.getLogger("optimization")
1112

1213

13-
@register_strategy("index-search")
14+
@register_strategy("index-search", schema=Schema([str]))
1415
class IndexSearch(OptimizationStrategy):
1516
# A task with no dependencies remaining after optimization will be replaced
1617
# if artifacts exist for the corresponding index_paths.
@@ -73,7 +74,7 @@ def should_replace_task(self, task, params, deadline, arg):
7374
return False
7475

7576

76-
@register_strategy("skip-unless-changed")
77+
@register_strategy("skip-unless-changed", schema=Schema([str]))
7778
class SkipUnlessChanged(OptimizationStrategy):
7879
def check(self, files_changed, patterns):
7980
for pattern in patterns:

src/taskgraph/transforms/task.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from taskgraph.util.hash import hash_path
2424
from taskgraph.util.keyed_by import evaluate_keyed_by
2525
from taskgraph.util.schema import (
26-
OptimizationSchema,
2726
Schema,
2827
optionally_keyed_by,
2928
resolve_keyed_by,
@@ -340,10 +339,10 @@ def run_task_suffix():
340339
description=dedent(
341340
"""
342341
Optimization to perform on this task during the optimization
343-
phase. Defined in taskcluster/taskgraph/optimize.py.
342+
phase. The schema for this value is specific to the given optimization.
344343
""".lstrip()
345344
),
346-
): OptimizationSchema,
345+
): Any(None, dict),
347346
Required(
348347
"worker-type",
349348
description=dedent(

src/taskgraph/util/schema.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -230,16 +230,6 @@ def __getitem__(self, item):
230230
return self.schema[item] # type: ignore
231231

232232

233-
OptimizationSchema = voluptuous.Any(
234-
# always run this task (default)
235-
None,
236-
# search the index for the given index namespaces, and replace this task if found
237-
# the search occurs in order, with the first match winning
238-
{"index-search": [str]},
239-
# skip this task if none of the given file patterns match
240-
{"skip-unless-changed": [str]},
241-
)
242-
243233
# shortcut for a string where task references are allowed
244234
taskref_or_string = voluptuous.Any(
245235
str,

test/test_optimize.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pytest
99
from pytest_taskgraph import make_graph, make_task
10+
from voluptuous import Schema
1011

1112
from taskgraph.graph import Graph
1213
from taskgraph.optimize import base as optimize_mod
@@ -487,3 +488,15 @@ def test_register_strategy(mocker):
487488
func = register_strategy("foo", args=("one", "two"), kwargs={"n": 1})
488489
func(m)
489490
m.assert_called_with("one", "two", n=1)
491+
492+
493+
def test_register_strategy_with_schema(mocker, monkeypatch):
494+
monkeypatch.setattr(optimize_mod, "registry", {})
495+
schema = Schema([str])
496+
497+
@register_strategy("bar", schema=schema)
498+
class TestStrategy(OptimizationStrategy):
499+
pass
500+
501+
assert "bar" in optimize_mod.registry
502+
assert optimize_mod.registry["bar"].schema is schema

0 commit comments

Comments
 (0)