hydraflow 0.10.1__py3-none-any.whl → 0.11.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hydraflow/cli.py CHANGED
@@ -2,54 +2,53 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Annotated
5
+ from typing import Annotated
6
6
 
7
7
  import typer
8
8
  from rich.console import Console
9
9
  from typer import Argument, Option
10
10
 
11
- from hydraflow.executor.io import load_config
12
-
13
- if TYPE_CHECKING:
14
- from hydraflow.executor.job import Job
11
+ from hydraflow.executor.io import get_job, load_config
15
12
 
16
13
  app = typer.Typer(add_completion=False)
17
14
  console = Console()
18
15
 
19
16
 
20
- def get_job(name: str) -> Job:
21
- cfg = load_config()
22
- job = cfg.jobs[name]
23
-
24
- if not job.name:
25
- job.name = name
26
-
27
- return job
28
-
29
-
30
17
  @app.command()
31
18
  def run(
32
19
  name: Annotated[str, Argument(help="Job name.", show_default=False)],
20
+ *,
21
+ dry_run: Annotated[
22
+ bool,
23
+ Option("--dry-run", help="Perform a dry run"),
24
+ ] = False,
33
25
  ) -> None:
34
26
  """Run a job."""
35
27
  import mlflow
36
28
 
37
- from hydraflow.executor.job import multirun
29
+ from hydraflow.executor.job import multirun, to_text
38
30
 
39
31
  job = get_job(name)
40
- mlflow.set_experiment(job.name)
41
- multirun(job)
32
+ if dry_run:
33
+ typer.echo(to_text(job))
34
+ else:
35
+ mlflow.set_experiment(job.name)
36
+ multirun(job)
42
37
 
43
38
 
44
39
  @app.command()
45
40
  def show(
46
- name: Annotated[str, Argument(help="Job name.", show_default=False)],
41
+ name: Annotated[str, Argument(help="Job name.", show_default=False)] = "",
47
42
  ) -> None:
48
- """Show a job."""
49
- from hydraflow.executor.job import show
43
+ """Show the hydraflow config."""
44
+ from omegaconf import OmegaConf
50
45
 
51
- job = get_job(name)
52
- show(job)
46
+ if name:
47
+ cfg = get_job(name)
48
+ else:
49
+ cfg = load_config()
50
+
51
+ typer.echo(OmegaConf.to_yaml(cfg))
53
52
 
54
53
 
55
54
  @app.callback(invoke_without_command=True)
@@ -5,9 +5,9 @@ from dataclasses import dataclass, field
5
5
 
6
6
  @dataclass
7
7
  class Step:
8
- args: str = ""
9
8
  batch: str = ""
10
- options: str = ""
9
+ args: str = ""
10
+ configs: str = ""
11
11
 
12
12
 
13
13
  @dataclass
@@ -15,6 +15,7 @@ class Job:
15
15
  name: str = ""
16
16
  run: str = ""
17
17
  call: str = ""
18
+ configs: str = ""
18
19
  steps: list[Step] = field(default_factory=list)
19
20
 
20
21
 
hydraflow/executor/io.py CHANGED
@@ -3,11 +3,15 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from pathlib import Path
6
+ from typing import TYPE_CHECKING
6
7
 
7
8
  from omegaconf import OmegaConf
8
9
 
9
10
  from .conf import HydraflowConf
10
11
 
12
+ if TYPE_CHECKING:
13
+ from .job import Job
14
+
11
15
 
12
16
  def find_config_file() -> Path | None:
13
17
  """Find the hydraflow config file."""
@@ -32,3 +36,14 @@ def load_config() -> HydraflowConf:
32
36
  cfg = OmegaConf.load(path)
33
37
 
34
38
  return OmegaConf.merge(schema, cfg) # type: ignore
39
+
40
+
41
+ def get_job(name: str) -> Job:
42
+ """Get a job from the config."""
43
+ cfg = load_config()
44
+ job = cfg.jobs[name]
45
+
46
+ if not job.name:
47
+ job.name = name
48
+
49
+ return job
hydraflow/executor/job.py CHANGED
@@ -12,7 +12,7 @@ The module supports two execution modes:
12
12
  2. Python function calls
13
13
 
14
14
  Each job can consist of multiple steps, and each step can have its own
15
- arguments and options that will be expanded into multiple runs.
15
+ arguments and configurations that will be expanded into multiple runs.
16
16
  """
17
17
 
18
18
  from __future__ import annotations
@@ -20,6 +20,7 @@ from __future__ import annotations
20
20
  import importlib
21
21
  import shlex
22
22
  import subprocess
23
+ import sys
23
24
  from subprocess import CalledProcessError
24
25
  from typing import TYPE_CHECKING
25
26
 
@@ -30,27 +31,27 @@ from .parser import collect, expand
30
31
  if TYPE_CHECKING:
31
32
  from collections.abc import Iterator
32
33
 
33
- from .conf import Job, Step
34
+ from .conf import Job
34
35
 
35
36
 
36
- def iter_args(step: Step) -> Iterator[list[str]]:
37
+ def iter_args(batch: str, args: str) -> Iterator[list[str]]:
37
38
  """Iterate over combinations generated from parsed arguments.
38
39
 
39
40
  Generate all possible combinations of arguments by parsing and
40
41
  expanding each one, yielding them as an iterator.
41
42
 
42
43
  Args:
43
- step (Step): The step to parse.
44
+ batch (str): The batch to parse.
45
+ args (str): The arguments to parse.
44
46
 
45
47
  Yields:
46
48
  list[str]: a list of the parsed argument combinations.
47
49
 
48
50
  """
49
- args = collect(step.args)
50
- options = [o for o in step.options.split(" ") if o]
51
+ args_ = collect(args)
51
52
 
52
- for batch in expand(step.batch):
53
- yield [*options, *sorted([*batch, *args])]
53
+ for batch_ in expand(batch):
54
+ yield [*batch_, *args_]
54
55
 
55
56
 
56
57
  def iter_batches(job: Job) -> Iterator[list[str]]:
@@ -68,11 +69,14 @@ def iter_batches(job: Job) -> Iterator[list[str]]:
68
69
 
69
70
  """
70
71
  job_name = f"hydra.job.name={job.name}"
72
+ job_configs = shlex.split(job.configs)
71
73
 
72
74
  for step in job.steps:
73
- for args in iter_args(step):
75
+ configs = shlex.split(step.configs) or job_configs
76
+
77
+ for args in iter_args(step.batch, step.args):
74
78
  sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ULID()}"
75
- yield ["--multirun", sweep_dir, job_name, *args]
79
+ yield ["--multirun", *args, job_name, sweep_dir, *configs]
76
80
 
77
81
 
78
82
  def multirun(job: Job) -> None:
@@ -99,6 +103,8 @@ def multirun(job: Job) -> None:
99
103
 
100
104
  if job.run:
101
105
  base_cmds = shlex.split(job.run)
106
+ if base_cmds[0] == "python" and sys.platform == "win32":
107
+ base_cmds[0] = sys.executable
102
108
 
103
109
  for args in it:
104
110
  cmds = [*base_cmds, *args]
@@ -132,24 +138,31 @@ def multirun(job: Job) -> None:
132
138
  raise RuntimeError(msg) from e
133
139
 
134
140
 
135
- def show(job: Job) -> None:
136
- """Show the job configuration.
141
+ def to_text(job: Job) -> str:
142
+ """Convert the job configuration to a string.
137
143
 
138
- This function shows the job configuration for a given job.
144
+ This function returns the job configuration for a given job.
139
145
 
140
146
  Args:
141
147
  job (Job): The job configuration to show.
142
148
 
149
+ Returns:
150
+ str: The job configuration.
151
+
143
152
  """
153
+ text = ""
154
+
144
155
  it = iter_batches(job)
145
156
 
146
157
  if job.run:
147
158
  base_cmds = shlex.split(job.run)
148
159
  for args in it:
149
160
  cmds = " ".join([*base_cmds, *args])
150
- print(cmds) # noqa: T201
161
+ text += f"{cmds}\n"
151
162
 
152
163
  elif job.call:
153
- print(f"call: {job.call}") # noqa: T201
164
+ text = f"call: {job.call}\n"
154
165
  for args in it:
155
- print(f"args: {args}") # noqa: T201
166
+ text += f"args: {args}\n"
167
+
168
+ return text.rstrip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.10.1
3
+ Version: 0.11.0
4
4
  Summary: Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments.
5
5
  Project-URL: Documentation, https://daizutabi.github.io/hydraflow/
6
6
  Project-URL: Source, https://github.com/daizutabi/hydraflow
@@ -1,5 +1,5 @@
1
1
  hydraflow/__init__.py,sha256=f2KO2iF7um-nNmayNyEr7TWG4UICOXy7YAN1d3qu0OY,936
2
- hydraflow/cli.py,sha256=gbDPj49azP8CCGxkxU0rksh1-gCyjP0VkVYH34ktcsA,1338
2
+ hydraflow/cli.py,sha256=b-M368amGUblOOqOi7JuBsnzLuaNqgpTit9ZRj0qqac,1410
3
3
  hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  hydraflow/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  hydraflow/core/config.py,sha256=SJzjgsO_kzB78_whJ3lmy7GlZvTvwZONH1BJBn8zCuI,3817
@@ -13,12 +13,12 @@ hydraflow/entities/run_collection.py,sha256=E8IRBgxCnJE_IPCaSmS2mc9GtDXXLBfc7GHv
13
13
  hydraflow/entities/run_data.py,sha256=Y2_Lc-BdQ7nXhcEIjdHGHIkLrXsmAktOftESEwYOY8o,1602
14
14
  hydraflow/entities/run_info.py,sha256=FRC6ICOlzB2u_xi_33Qs-YZLt677UotuNbYqI7XSmHY,1017
15
15
  hydraflow/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- hydraflow/executor/conf.py,sha256=q_FrPXQJCVGKS1FYnGRGqTUgMQeMBkaVPW2mtQc8oxk,384
17
- hydraflow/executor/io.py,sha256=4nafwge6vHanYFuEHxd0LRv_3ZLgMpV50qSbssZNe3Q,696
18
- hydraflow/executor/job.py,sha256=_f_7lfPsPMGBNB7ZW50-MCZX-BQi-s0_Ajg3DjqHx5s,4553
16
+ hydraflow/executor/conf.py,sha256=SJNiQ87MXMlpDfdm0POcv55MY3GS5FUh5wT7u3XU3oU,406
17
+ hydraflow/executor/io.py,sha256=xV3m-nV9eKbu9Fb7u04J2bfmR_Ky3jTEJjq4QC2m6V4,954
18
+ hydraflow/executor/job.py,sha256=yiVbAYgsZZtSTRER-H5pUopULeygzPNzKlSkl27yFI4,4856
19
19
  hydraflow/executor/parser.py,sha256=MO8VU0uVQZeku6kbw8Urid_5QEcnR8atd5h-yDP5OhQ,10147
20
- hydraflow-0.10.1.dist-info/METADATA,sha256=1fBBNz3-3gczIXYs9tq7lvzDySjz1BlFMQsO73KgCrg,4574
21
- hydraflow-0.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- hydraflow-0.10.1.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
- hydraflow-0.10.1.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
- hydraflow-0.10.1.dist-info/RECORD,,
20
+ hydraflow-0.11.0.dist-info/METADATA,sha256=MIDZ7Vec2SDAcj0S1SvghtlN5RQTpfR1N3MLlwoLD9A,4574
21
+ hydraflow-0.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ hydraflow-0.11.0.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
+ hydraflow-0.11.0.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
+ hydraflow-0.11.0.dist-info/RECORD,,