Skip to content

Commit 96d9afa

Browse files
committed
Attempt to break circularity
1 parent 20ebde6 commit 96d9afa

6 files changed

Lines changed: 45 additions & 36 deletions

File tree

pangeo_forge_runner/bakery/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from typing import List
22

33
from apache_beam.pipeline import Pipeline, PipelineOptions
4-
from traitlets import TraitError
4+
from traitlets import HasTraits, TraitError
55
from traitlets.config import LoggingConfigurable
66

7-
from ..commands.bake import Bake, ExecutionMetadata
7+
from .execution_metadata import ExecutionMetadata
88

99

1010
class Bakery(LoggingConfigurable):
@@ -40,12 +40,12 @@ def bake(self, pipeline: Pipeline, meta: ExecutionMetadata) -> None:
4040
result = pipeline.run()
4141
job_id = result.job_id()
4242
self.log.info(
43-
f"Submitted job {meta.job_id} for recipe {meta.name}",
43+
f"Submitted job {meta.job_id} for recipe {meta.recipe_name}",
4444
extra=meta.to_dict() | {"job_id": job_id, "status": "submitted"},
4545
)
4646

4747
@classmethod
48-
def validate_bake_command(cls, bake_command: Bake) -> List[TraitError]:
48+
def validate_bake_command(cls, bake_command: HasTraits) -> List[TraitError]:
4949
"""
5050
Validates the given bake_command and collects any validation errors.
5151
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from dataclasses import asdict, dataclass
2+
3+
4+
@dataclass
5+
class ExecutionMetadata:
6+
"""
7+
Holds metadata for an execution instance, including recipe and job names.
8+
9+
Attributes:
10+
recipe_name (str): Name of the recipe being executed.
11+
job_name (str): Unique name for the job execution.
12+
"""
13+
14+
recipe_name: str
15+
job_name: str
16+
17+
def to_dict(self) -> dict:
18+
return asdict(self)

pangeo_forge_runner/bakery/flink.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
import escapism
1515
from apache_beam.pipeline import Pipeline, PipelineOptions
16-
from traitlets import Bool, Dict, Integer, TraitError, Unicode
16+
from traitlets import Bool, Dict, HasTraits, Integer, TraitError, Unicode
1717

18-
from ..commands.bake import Bake, ExecutionMetadata
1918
from .base import Bakery
19+
from .execution_metadata import ExecutionMetadata
2020

2121

2222
# Copied from https://github.com/jupyterhub/kubespawner/blob/7d6d82c2be469dd76f770d6f6ed0d1dade6b24a7/kubespawner/utils.py#L8
@@ -380,15 +380,15 @@ def bake(self, pipeline: Pipeline, meta: ExecutionMetadata) -> None:
380380
meta (ExecutionMetadata): An instance of BakeMetadata containing metadata about the bake process.
381381
"""
382382
self.log.info(
383-
f"Running flink job for recipe {meta.name}\n",
383+
f"Running flink job for recipe {meta.recipe_name}\n",
384384
extra=meta.to_dict() | {"status": "running"},
385385
)
386386
pipeline.run()
387387

388388
@classmethod
389-
def validate_bake_command(cls, bake_command: Bake) -> List[TraitError]:
389+
def validate_bake_command(cls, bake_command: HasTraits) -> List[TraitError]:
390390
errors = []
391-
if not bake_command.container_image:
391+
if not bake_command._trait_values["container_image"]:
392392
errors.append(
393393
TraitError(
394394
"'container_image' is required when using the 'FlinkOperatorBakery' "

pangeo_forge_runner/bakery/local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from apache_beam.pipeline import Pipeline, PipelineOptions
66
from traitlets import Integer
77

8-
from ..commands.bake import ExecutionMetadata
98
from .base import Bakery
9+
from .execution_metadata import ExecutionMetadata
1010

1111

1212
class LocalDirectBakery(Bakery):
@@ -54,7 +54,7 @@ def bake(self, pipeline: Pipeline, meta: ExecutionMetadata) -> None:
5454
meta (ExecutionMetadata): An instance of BakeMetadata containing metadata about the bake process.
5555
"""
5656
self.log.info(
57-
f"Running local job for recipe {meta.name}\n",
57+
f"Running local job for recipe {meta.recipe_name}\n",
5858
extra=meta.to_dict() | {"status": "running"},
5959
)
6060
pipeline.run()

pangeo_forge_runner/commands/bake.py

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import re
88
import string
99
import time
10-
from dataclasses import asdict, dataclass
1110
from importlib.metadata import distributions
1211
from pathlib import Path
1312

@@ -17,30 +16,14 @@
1716

1817
from .. import Feedstock
1918
from ..bakery.base import Bakery
19+
from ..bakery.execution_metadata import ExecutionMetadata
2020
from ..bakery.local import LocalDirectBakery
2121
from ..plugin import get_injections, get_injectionspecs_from_entrypoints
2222
from ..storage import InputCacheStorage, TargetStorage
2323
from ..stream_capture import redirect_stderr, redirect_stdout
2424
from .base import BaseCommand, common_aliases, common_flags
2525

2626

27-
@dataclass
28-
class ExecutionMetadata:
29-
"""
30-
Holds metadata for an execution instance, including recipe and job names.
31-
32-
Attributes:
33-
recipe_name (str): Name of the recipe being executed.
34-
job_name (str): Unique name for the job execution.
35-
"""
36-
37-
recipe_name: str
38-
job_name: str
39-
40-
def to_dict(self) -> dict:
41-
return asdict(self)
42-
43-
4427
class Bake(BaseCommand):
4528
"""
4629
Command to bake a pangeo forge recipe in a given bakery
@@ -138,8 +121,7 @@ def _validate_job_name(self, proposal):
138121
)
139122
return proposal.value
140123

141-
@validate("bakery_class")
142-
def _bakery_impl_validation(self, proposal):
124+
def bakery_impl_validation(self):
143125
"""
144126
Validates the 'bakery_class' trait using class-specific validation logic.
145127
@@ -150,8 +132,11 @@ def _bakery_impl_validation(self, proposal):
150132
Raises:
151133
- TraitError: If multiple validation errors are encountered, a single TraitError
152134
is raised containing a summary of all error messages.
135+
136+
Note: Traitlets has no convenient way to set initialization hooks (???) so we have
137+
to run this manually.
153138
"""
154-
klass = proposal["value"]
139+
klass = self.bakery_class
155140
if errors := klass.validate_bake_command(self):
156141
if len(errors) == 1:
157142
raise errors[0]
@@ -202,6 +187,9 @@ def start(self):
202187
"""
203188
Start the baking process
204189
"""
190+
# Validate bakery-specific requirements of this class. Yes, we have to do it here.
191+
self.bakery_impl_validation()
192+
205193
if not "pangeo-forge-recipes" in [d.metadata["Name"] for d in distributions()]:
206194
raise ValueError(
207195
"To use the `bake` command, `pangeo-forge-recipes` must be installed."

tests/unit/test_bake.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import pytest
1111
import xarray as xr
1212
from packaging.version import parse as parse_version
13+
from traitlets import TraitError
1314

1415
from pangeo_forge_runner.commands.bake import Bake
1516

@@ -72,12 +73,13 @@ def test_job_name_validation(job_name, raises):
7273
bake = Bake()
7374
if raises:
7475
with pytest.raises(
75-
ValueError,
76+
TraitError,
7677
match=re.escape(
7778
f"job_name must match the regex ^[a-z][-_0-9a-z]{{0,62}}$, instead found {job_name}"
7879
),
7980
):
8081
bake.job_name = job_name
82+
bake.bakery_impl_validation()
8183
else:
8284
bake.job_name = job_name
8385
assert bake.job_name == job_name
@@ -90,15 +92,16 @@ def test_job_name_validation(job_name, raises):
9092
["apache/beam_python3.10_sdk:2.51.0", False],
9193
),
9294
)
93-
def test_container_name_validation(container_image, raises):
95+
def test_container_image_validation(container_image, raises):
9496
bake = Bake()
9597
if raises:
9698
with pytest.raises(
97-
ValueError,
98-
match=r"^'container_name' is required.*",
99+
TraitError,
100+
match=r"^'container_image' is required.*",
99101
):
100102
bake.bakery_class = "pangeo_forge_runner.bakery.flink.FlinkOperatorBakery"
101103
bake.container_image = container_image
104+
bake.bakery_impl_validation()
102105
else:
103106
bake.bakery_class = "pangeo_forge_runner.bakery.flink.FlinkOperatorBakery"
104107
bake.container_image = container_image

0 commit comments

Comments
 (0)