Skip to content

Commit 841534b

Browse files
committed
refactor: [config] move the flow config into the project config
This means the project config contains the whole project configuration including the flow config. This can be broken down later into smaller Pydantic objects as the fields and types become better defined. Signed-off-by: James McCorrie <[email protected]>
1 parent f32361e commit 841534b

File tree

4 files changed

+110
-59
lines changed

4 files changed

+110
-59
lines changed

src/dvsim/cli.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222

2323
import argparse
2424
import datetime
25-
import json
2625
import os
2726
import random
2827
import sys
2928
import textwrap
30-
from collections.abc import Sequence
3129
from pathlib import Path
3230

3331
from dvsim.flow.factory import make_flow
@@ -671,6 +669,8 @@ def main() -> None:
671669
purge=args.purge,
672670
dry_run=args.dry_run,
673671
remote=args.remote,
672+
select_cfgs=args.select_cfgs,
673+
args=args,
674674
)
675675
project_cfg.save()
676676

@@ -701,16 +701,8 @@ def main() -> None:
701701

702702
# Build infrastructure from hjson file and create the list of items to
703703
# be deployed.
704-
config_data = project_cfg.load_config(
705-
select_cfgs=args.select_cfgs,
706-
args=args,
707-
)
708-
project_cfg.run_dir.mkdir(parents=True, exist_ok=True)
709-
(project_cfg.run_dir / "config.json").write_text(json.dumps(config_data))
710-
711704
flow = make_flow(
712705
project_cfg=project_cfg,
713-
config_data=config_data,
714706
args=args,
715707
)
716708

src/dvsim/flow/factory.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"""Factory to generate a flow config."""
55

66
from argparse import Namespace
7-
from collections.abc import Mapping
87

98
from dvsim.flow.base import FlowCfg
109
from dvsim.flow.cdc import CdcCfg
@@ -53,7 +52,6 @@ def _get_flow_handler_cls(flow: str) -> type[FlowCfg]:
5352

5453
def make_flow(
5554
project_cfg: Project,
56-
config_data: Mapping,
5755
args: Namespace,
5856
) -> FlowCfg:
5957
"""Make a flow config by loading the config file at path.
@@ -68,6 +66,9 @@ def make_flow(
6866
config file.
6967
7068
"""
69+
# convert back to simple dict
70+
config_data = project_cfg.config.model_dump()
71+
7172
if "flow" not in config_data:
7273
msg = 'No value for the "flow" key. Are you sure this is a dvsim configuration file?'
7374
raise RuntimeError(

src/dvsim/project.py

Lines changed: 81 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ class FlowConfig(BaseModel):
2929

3030
model_config = ConfigDict(frozen=True, extra="allow")
3131

32+
flow: str
33+
name: str
34+
35+
36+
class TopFlowConfig(BaseModel):
37+
"""Flow configuration data."""
38+
39+
model_config = ConfigDict(frozen=True, extra="allow")
40+
41+
flow: str
42+
project: str
43+
revision: str
44+
45+
cfgs: Mapping[Path, FlowConfig]
46+
3247

3348
class Project(BaseModel):
3449
"""Project meta data."""
@@ -41,6 +56,7 @@ class Project(BaseModel):
4156
scratch_path: Path
4257
branch: str
4358
job_prefix: str
59+
config: TopFlowConfig | FlowConfig
4460

4561
logfile: Path
4662
run_dir: Path
@@ -55,41 +71,6 @@ def save(self) -> None:
5571

5672
(self.run_dir / "project.json").write_text(meta_json)
5773

58-
def load_config(
59-
self,
60-
select_cfgs: Sequence[str] | None,
61-
args: Namespace,
62-
) -> Mapping:
63-
"""Load the project configuration.
64-
65-
Args:
66-
project_cfg: metadata about the project
67-
select_cfgs: list of child config names to use from the primary config
68-
args: are the arguments passed to the CLI
69-
70-
Returns:
71-
Project configuration.
72-
73-
"""
74-
log.info("Loading primary config file: %s", self.top_cfg_path)
75-
76-
# load the whole project config data
77-
cfg = dict(
78-
load_cfg(
79-
path=self.top_cfg_path,
80-
path_resolution_wildcards={
81-
"proj_root": self.root_path,
82-
},
83-
select_cfgs=select_cfgs,
84-
),
85-
)
86-
87-
# Tool specified on CLI overrides the file based config
88-
if args.tool is not None:
89-
cfg["tool"] = args.tool
90-
91-
return cfg
92-
9374
@staticmethod
9475
def load(path: Path) -> "Project":
9576
"""Load project meta from file."""
@@ -102,6 +83,8 @@ def init(
10283
proj_root: Path | None,
10384
scratch_root: Path | None,
10485
branch: str,
86+
select_cfgs: Sequence[str] | None,
87+
args: Namespace,
10588
*,
10689
job_prefix: str = "",
10790
purge: bool = False,
@@ -118,6 +101,19 @@ def init(
118101
This function returns the updated proj_root src and destination path. If
119102
--remote switch is not set, the destination path is identical to the src
120103
path. Likewise, if --dry-run is set.
104+
105+
Args:
106+
args: are the arguments passed to the CLI
107+
branch: version control branch
108+
cfg_path: path to the top flow config
109+
dry_run: do not run any jobs just go through the motions
110+
job_prefix: prefix for the job name
111+
proj_root: path to the project root
112+
purge: bool = False,
113+
remote: remote execution
114+
scratch_root: path to the scratch dir
115+
select_cfgs: list of child config names to use from the primary config
116+
121117
"""
122118
if not cfg_path.exists():
123119
log.fatal("Path to config file %s appears to be invalid.", cfg_path)
@@ -156,6 +152,14 @@ def init(
156152
cfg_path = root_path / cfg_path.relative_to(src_path)
157153

158154
run_dir = scratch_path / branch
155+
156+
config = _load_flow_config(
157+
top_cfg_path=cfg_path,
158+
root_path=root_path,
159+
select_cfgs=select_cfgs,
160+
args=args,
161+
)
162+
159163
return Project(
160164
top_cfg_path=cfg_path,
161165
root_path=root_path,
@@ -165,9 +169,51 @@ def init(
165169
job_prefix=job_prefix,
166170
logfile=run_dir / "run.log",
167171
run_dir=run_dir,
172+
config=config,
168173
)
169174

170175

176+
def _load_flow_config(
177+
top_cfg_path: Path,
178+
root_path: Path,
179+
select_cfgs: Sequence[str] | None,
180+
args: Namespace,
181+
) -> TopFlowConfig | FlowConfig:
182+
"""Load the project configuration.
183+
184+
Args:
185+
top_cfg_path: path to the top level config file
186+
root_path: path to the root of the project,
187+
select_cfgs: list of child config names to use from the primary config
188+
args: are the arguments passed to the CLI
189+
190+
Returns:
191+
Project configuration.
192+
193+
"""
194+
log.info("Loading primary config file: %s", top_cfg_path)
195+
196+
# load the whole project config data
197+
cfg = dict(
198+
load_cfg(
199+
path=top_cfg_path,
200+
path_resolution_wildcards={
201+
"proj_root": root_path,
202+
},
203+
select_cfgs=select_cfgs,
204+
),
205+
)
206+
207+
# Tool specified on CLI overrides the file based config
208+
if args.tool is not None:
209+
cfg["tool"] = args.tool
210+
211+
if "cfgs" in cfg:
212+
return TopFlowConfig.model_validate(cfg)
213+
214+
return FlowConfig.model_validate(cfg)
215+
216+
171217
def _network_dir_accessible_and_exists(
172218
path: Path,
173219
timeout: int = 1,

tests/test_project.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,38 @@
88
from pathlib import Path
99

1010
import pytest
11-
from hamcrest import assert_that, equal_to
11+
from hamcrest import assert_that, equal_to, instance_of
1212

13-
from dvsim.project import Project
13+
from dvsim.project import FlowConfig, Project, TopFlowConfig
1414

1515
__all__ = ()
1616

1717

1818
@pytest.mark.parametrize(
19-
"data",
19+
("data", "flow_config_cls"),
2020
[
21-
{
22-
"top_cfg_path": Path("cfg_path.hjson"),
23-
"root_path": Path("root_path"),
24-
"src_path": Path("src_path"),
25-
"branch": "branch",
26-
"job_prefix": "job_prefix",
27-
"logfile": Path("logfile"),
28-
},
21+
(
22+
{
23+
"top_cfg_path": Path("cfg_path.hjson"),
24+
"root_path": Path("root_path"),
25+
"src_path": Path("src_path"),
26+
"branch": "branch",
27+
"job_prefix": "job_prefix",
28+
"logfile": Path("logfile"),
29+
"config": {
30+
"flow": "flow",
31+
"name": "name",
32+
},
33+
},
34+
FlowConfig,
35+
),
2936
],
3037
)
31-
def test_project_meta(data: Mapping, tmp_path: Path) -> None:
38+
def test_project_config(
39+
data: Mapping,
40+
flow_config_cls: type[FlowConfig | TopFlowConfig],
41+
tmp_path: Path,
42+
) -> None:
3243
"""Test Project saving and loading."""
3344
meta = Project(
3445
**data,
@@ -41,3 +52,4 @@ def test_project_meta(data: Mapping, tmp_path: Path) -> None:
4152
loaded_meta = Project.load(path=meta.run_dir)
4253

4354
assert_that(loaded_meta, equal_to(meta))
55+
assert_that(loaded_meta.config, instance_of(flow_config_cls))

0 commit comments

Comments
 (0)