Skip to content

Commit 60318b4

Browse files
feat: used schema wrapper for msgspec
1 parent d6f3589 commit 60318b4

File tree

13 files changed

+198
-378
lines changed

13 files changed

+198
-378
lines changed

src/taskgraph/config.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import msgspec
1414

1515
from .util.python_path import find_object
16-
from .util.schema import validate_schema
16+
from .util.schema import Schema, validate_schema
1717
from .util.vcs import get_repository
1818
from .util.yaml import load_yaml
1919

@@ -26,7 +26,7 @@
2626
]
2727

2828

29-
class WorkerAlias(msgspec.Struct, kw_only=True, rename="kebab"):
29+
class WorkerAlias(Schema):
3030
"""Worker alias configuration."""
3131

3232
provisioner: Union[str, dict]
@@ -35,32 +35,38 @@ class WorkerAlias(msgspec.Struct, kw_only=True, rename="kebab"):
3535
worker_type: Union[str, dict] # Can be keyed-by, maps from "worker-type"
3636

3737

38-
class Workers(msgspec.Struct, kw_only=True):
38+
class Workers(Schema, rename=None):
3939
"""Workers configuration."""
4040

4141
aliases: Dict[str, WorkerAlias]
4242

4343

44-
class Repository(msgspec.Struct, kw_only=True, rename="kebab"):
44+
class Repository(Schema):
4545
"""Repository configuration."""
4646

47+
# Required fields first
4748
name: str
49+
50+
# Optional fields
4851
project_regex: Optional[str] = None # Maps from "project-regex"
4952
ssh_secret_name: Optional[str] = None # Maps from "ssh-secret-name"
5053
# Allow extra fields for flexibility
5154
__extras__: Dict[str, Any] = msgspec.field(default_factory=dict)
5255

5356

54-
class RunConfig(msgspec.Struct, kw_only=True, rename="kebab"):
57+
class RunConfig(Schema):
5558
"""Run transforms configuration."""
5659

5760
use_caches: Optional[Union[bool, List[str]]] = None # Maps from "use-caches"
5861

5962

60-
class TaskGraphConfig(msgspec.Struct, kw_only=True, rename="kebab"):
63+
class TaskGraphConfig(Schema):
6164
"""Taskgraph specific configuration."""
6265

66+
# Required fields first
6367
repositories: Dict[str, Repository]
68+
69+
# Optional fields
6470
register: Optional[str] = None
6571
decision_parameters: Optional[str] = None # Maps from "decision-parameters"
6672
cached_task_prefix: Optional[str] = None # Maps from "cached-task-prefix"
@@ -69,17 +75,18 @@ class TaskGraphConfig(msgspec.Struct, kw_only=True, rename="kebab"):
6975
run: Optional[RunConfig] = None
7076

7177

72-
class GraphConfigSchema(
73-
msgspec.Struct, kw_only=True, omit_defaults=True, rename="kebab"
74-
):
78+
class GraphConfigSchema(Schema):
7579
"""Main graph configuration schema."""
7680

81+
# Required fields first
7782
trust_domain: str # Maps from "trust-domain"
7883
task_priority: Union[
7984
TaskPriority, dict
8085
] # Maps from "task-priority", can be keyed-by
8186
workers: Workers
8287
taskgraph: TaskGraphConfig
88+
89+
# Optional fields
8390
docker_image_kind: Optional[str] = None # Maps from "docker-image-kind"
8491
task_deadline_after: Optional[Union[str, dict]] = (
8592
None # Maps from "task-deadline-after", can be keyed-by
@@ -161,9 +168,7 @@ def kinds_dir(self):
161168
def validate_graph_config(config):
162169
"""Validate graph configuration using msgspec."""
163170
# With rename="kebab", msgspec handles the conversion automatically
164-
validate_schema(
165-
GraphConfigSchema, config, "Invalid graph configuration:", use_msgspec=True
166-
)
171+
validate_schema(GraphConfigSchema, config, "Invalid graph configuration:")
167172

168173

169174
def load_graph_config(root_dir):

src/taskgraph/decision.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from pathlib import Path
1212
from typing import Any, Dict, Optional
1313

14-
import msgspec
1514
import yaml
1615

1716
from taskgraph.actions import render_actions_json
@@ -21,7 +20,7 @@
2120
from taskgraph.taskgraph import TaskGraph
2221
from taskgraph.util import json
2322
from taskgraph.util.python_path import find_object
24-
from taskgraph.util.schema import validate_schema
23+
from taskgraph.util.schema import Schema, validate_schema
2524
from taskgraph.util.vcs import Repository, get_repository
2625
from taskgraph.util.yaml import load_yaml
2726

@@ -41,7 +40,8 @@
4140

4241

4342
#: Schema for try_task_config.json version 2
44-
class TryTaskConfigSchemaV2(msgspec.Struct, kw_only=True, omit_defaults=True):
43+
class TryTaskConfigSchemaV2(Schema):
44+
# All fields are optional
4545
parameters: Optional[Dict[str, Any]] = None
4646

4747

@@ -358,7 +358,6 @@ def set_try_config(parameters, task_config_file):
358358
try_task_config_schema_v2,
359359
task_config,
360360
"Invalid v2 `try_task_config.json`.",
361-
use_msgspec=True,
362361
)
363362
parameters.update(task_config["parameters"])
364363
return

src/taskgraph/parameters.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from taskgraph.util import json, yaml
2222
from taskgraph.util.readonlydict import ReadOnlyDict
23+
from taskgraph.util.schema import Schema
2324
from taskgraph.util.taskcluster import find_task_id, get_artifact_url
2425
from taskgraph.util.vcs import get_repository
2526

@@ -28,20 +29,22 @@ class ParameterMismatch(Exception):
2829
"""Raised when a parameters.yml has extra or missing parameters."""
2930

3031

31-
class CodeReviewConfig(msgspec.Struct, kw_only=True, rename="kebab"):
32+
class CodeReviewConfig(Schema):
3233
"""Code review configuration."""
3334

35+
# Required field
3436
phabricator_build_target: str
3537

3638

3739
#: Schema for base parameters.
3840
#: Please keep this list sorted and in sync with docs/reference/parameters.rst
39-
class BaseSchema(msgspec.Struct, kw_only=True, omit_defaults=True, rename="kebab"):
41+
class BaseSchema(Schema):
4042
"""Base parameters schema.
4143
4244
This defines the core parameters that all taskgraph runs require.
4345
"""
4446

47+
# Required fields (most are required)
4548
base_repository: str
4649
base_ref: str
4750
base_rev: str
@@ -58,8 +61,6 @@ class BaseSchema(msgspec.Struct, kw_only=True, omit_defaults=True, rename="kebab
5861
head_tag: str
5962
level: str
6063
moz_build_date: str
61-
next_version: Optional[str]
62-
optimize_strategies: Optional[str]
6364
optimize_target_tasks: bool
6465
owner: str
6566
project: str
@@ -70,6 +71,10 @@ class BaseSchema(msgspec.Struct, kw_only=True, omit_defaults=True, rename="kebab
7071
# used at run-time
7172
target_tasks_method: str
7273
tasks_for: str
74+
75+
# Optional fields
76+
next_version: Optional[str]
77+
optimize_strategies: Optional[str]
7378
version: Optional[str]
7479
code_review: Optional[CodeReviewConfig] = None
7580

@@ -157,7 +162,7 @@ def extend_parameters_schema(schema, defaults_fn=None):
157162
graph-configuration.
158163
159164
Args:
160-
schema: The schema object (dict or msgspec) used to describe extended
165+
schema: The schema object (msgspec) used to describe extended
161166
parameters.
162167
defaults_fn (function): A function which takes no arguments and returns a
163168
dict mapping parameter name to default value in the
@@ -170,9 +175,8 @@ def extend_parameters_schema(schema, defaults_fn=None):
170175
# Store the extension schema for use during validation
171176
_schema_extensions.append(schema)
172177

173-
# Also extend the base_schema if it's a Schema instance
174-
if hasattr(base_schema, "extend"):
175-
base_schema = base_schema.extend(schema)
178+
# Schema extension is no longer supported with msgspec.Struct inheritance
179+
# Extensions are tracked in _schema_extensions list instead
176180

177181
if defaults_fn:
178182
defaults_functions.append(defaults_fn)

src/taskgraph/transforms/base.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
from dataclasses import dataclass, field
99
from typing import Dict, List, Union
1010

11-
import msgspec
12-
1311
from taskgraph.task import Task
1412

1513
from ..config import GraphConfig
@@ -156,9 +154,5 @@ def __call__(self, config, tasks):
156154
)
157155
else:
158156
error = "In unknown task:"
159-
# Check if schema is a msgspec.Struct type
160-
use_msgspec = isinstance(self.schema, type) and issubclass(
161-
self.schema, msgspec.Struct
162-
)
163-
validate_schema(self.schema, task, error, use_msgspec=use_msgspec)
157+
validate_schema(self.schema, task, error)
164158
yield task

src/taskgraph/transforms/run/__init__.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030

3131
# Fetches schema using msgspec
32-
class FetchesSchema(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=True):
32+
class FetchesSchema(Schema):
3333
"""Schema for fetch configuration."""
3434

3535
artifact: str
@@ -39,14 +39,14 @@ class FetchesSchema(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=
3939

4040

4141
# When configuration using msgspec
42-
class WhenConfig(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=True):
42+
class WhenConfig(Schema):
4343
"""Configuration for when a task should be included."""
4444

4545
files_changed: List[str] = msgspec.field(default_factory=list)
4646

4747

4848
# Run configuration using msgspec
49-
class RunConfig(msgspec.Struct, kw_only=True, omit_defaults=True):
49+
class RunConfig(Schema, rename=None):
5050
"""Configuration for how to run a task."""
5151

5252
using: str
@@ -56,20 +56,19 @@ class RunConfig(msgspec.Struct, kw_only=True, omit_defaults=True):
5656

5757

5858
# Run description schema using msgspec
59-
class RunDescriptionSchema(
60-
msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=True
61-
):
59+
class RunDescriptionSchema(Schema):
6260
"""Schema for run transforms."""
6361

64-
# Task naming
65-
name: TOptional[str] = None
66-
label: TOptional[str] = None
67-
68-
# Required fields
62+
# Required fields first
6963
description: str
7064
run: RunConfig
7165
worker_type: str
7266

67+
# Optional fields
68+
# Task naming
69+
name: TOptional[str] = None
70+
label: TOptional[str] = None
71+
7372
# Optional fields from task description
7473
priority: TOptional[str] = None
7574
attributes: Dict[str, Any] = msgspec.field(default_factory=dict)
@@ -104,7 +103,7 @@ class RunDescriptionSchema(
104103
fetches_schema = FetchesSchema
105104

106105
#: Schema for a run transforms - now using msgspec
107-
run_description_schema = Schema(RunDescriptionSchema)
106+
run_description_schema = RunDescriptionSchema
108107

109108

110109
transforms = TransformSequence()
@@ -399,15 +398,13 @@ def wrap(func):
399398

400399

401400
# Simple schema for always-optimized
402-
class AlwaysOptimizedRunSchema(msgspec.Struct, kw_only=True):
401+
class AlwaysOptimizedRunSchema(Schema, omit_defaults=False):
403402
"""Schema for always-optimized run tasks."""
404403

405404
using: str = "always-optimized"
406405

407406

408-
@run_task_using(
409-
"always-optimized", "always-optimized", Schema(AlwaysOptimizedRunSchema)
410-
)
407+
@run_task_using("always-optimized", "always-optimized", AlwaysOptimizedRunSchema)
411408
def always_optimized(config, task, taskdesc):
412409
pass
413410

src/taskgraph/transforms/run/index_search.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010

1111
from typing import List
1212

13-
import msgspec
14-
1513
from taskgraph.transforms.base import TransformSequence
1614
from taskgraph.transforms.run import run_task_using
15+
from taskgraph.util.schema import Schema
1716

1817
transforms = TransformSequence()
1918

2019

2120
#: Schema for run.using index-search
22-
class RunTaskSchema(msgspec.Struct, kw_only=True, rename="kebab"):
21+
class RunTaskSchema(Schema):
2322
using: str
2423
# A list of indexes in decreasing order of priority at which to lookup for this
2524
# task. This is interpolated with the graph parameters.

src/taskgraph/transforms/run/run_task.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import os
1010
from typing import Dict, List, Literal, Optional, Union
1111

12-
import msgspec
13-
1412
from taskgraph.transforms.run import run_task_using
1513
from taskgraph.transforms.run.common import (
1614
support_caches,
@@ -26,14 +24,24 @@
2624

2725

2826
#: Schema for run.using run_task
29-
class RunTaskSchema(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=True):
27+
class RunTaskSchema(Schema):
3028
"""
3129
Schema for run.using run_task.
3230
"""
3331

32+
# Required fields first
3433
# Specifies the task type. Must be 'run-task'.
3534
using: Literal["run-task"]
3635

36+
# The command arguments to pass to the `run-task` script, after the checkout
37+
# arguments. If a list, it will be passed directly; otherwise it will be
38+
# included in a single argument to the command specified by `exec-with`.
39+
command: Union[List[Union[str, Dict[str, str]]], str, Dict[str, str]]
40+
41+
# Base work directory used to set up the task.
42+
workdir: str
43+
44+
# Optional fields
3745
# Specifies which caches to use. May take a boolean in which case either all
3846
# (True) or no (False) caches will be used. Alternatively, it can accept a
3947
# list of caches to enable. Defaults to only the checkout cache enabled.
@@ -51,11 +59,6 @@ class RunTaskSchema(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=
5159
# directory where sparse profiles are defined (build/sparse-profiles/).
5260
sparse_profile: Optional[str] = None
5361

54-
# The command arguments to pass to the `run-task` script, after the checkout
55-
# arguments. If a list, it will be passed directly; otherwise it will be
56-
# included in a single argument to the command specified by `exec-with`.
57-
command: Union[List[Union[str, Dict[str, str]]], str, Dict[str, str]]
58-
5962
# Specifies what to execute the command with in the event the command is a
6063
# string.
6164
exec_with: Optional[Literal["bash", "powershell"]] = None
@@ -64,14 +67,11 @@ class RunTaskSchema(msgspec.Struct, kw_only=True, rename="kebab", omit_defaults=
6467
# or Python installation is in a non-standard location on the workers.
6568
run_task_command: Optional[List[str]] = None
6669

67-
# Base work directory used to set up the task.
68-
workdir: str
69-
7070
# Whether to run as root. Defaults to False.
7171
run_as_root: bool = False
7272

7373

74-
run_task_schema = Schema(RunTaskSchema)
74+
run_task_schema = RunTaskSchema
7575

7676

7777
def common_setup(config, task, taskdesc, command):

0 commit comments

Comments
 (0)