hydraflow 0.10.1__tar.gz → 0.10.2__tar.gz

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.
Files changed (95) hide show
  1. {hydraflow-0.10.1 → hydraflow-0.10.2}/.gitignore +1 -1
  2. {hydraflow-0.10.1 → hydraflow-0.10.2}/PKG-INFO +1 -1
  3. {hydraflow-0.10.1 → hydraflow-0.10.2}/pyproject.toml +1 -1
  4. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/cli.py +22 -23
  5. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/executor/io.py +15 -0
  6. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/executor/job.py +16 -6
  7. hydraflow-0.10.2/tests/cli/test_run.py +41 -0
  8. hydraflow-0.10.2/tests/cli/test_show.py +18 -0
  9. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/test_job.py +7 -12
  10. hydraflow-0.10.1/tests/cli/test_run.py +0 -33
  11. hydraflow-0.10.1/tests/cli/test_show.py +0 -23
  12. {hydraflow-0.10.1 → hydraflow-0.10.2}/.cursorrules +0 -0
  13. {hydraflow-0.10.1 → hydraflow-0.10.2}/.devcontainer/devcontainer.json +0 -0
  14. {hydraflow-0.10.1 → hydraflow-0.10.2}/.devcontainer/postCreate.sh +0 -0
  15. {hydraflow-0.10.1 → hydraflow-0.10.2}/.devcontainer/starship.toml +0 -0
  16. {hydraflow-0.10.1 → hydraflow-0.10.2}/.gitattributes +0 -0
  17. {hydraflow-0.10.1 → hydraflow-0.10.2}/.github/workflows/ci.yaml +0 -0
  18. {hydraflow-0.10.1 → hydraflow-0.10.2}/.github/workflows/docs.yaml +0 -0
  19. {hydraflow-0.10.1 → hydraflow-0.10.2}/LICENSE +0 -0
  20. {hydraflow-0.10.1 → hydraflow-0.10.2}/README.md +0 -0
  21. {hydraflow-0.10.1 → hydraflow-0.10.2}/apps/quickstart.py +0 -0
  22. {hydraflow-0.10.1 → hydraflow-0.10.2}/docs/index.md +0 -0
  23. {hydraflow-0.10.1 → hydraflow-0.10.2}/docs/usage/quickstart.md +0 -0
  24. {hydraflow-0.10.1 → hydraflow-0.10.2}/mkdocs.yaml +0 -0
  25. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/__init__.py +0 -0
  26. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/__init__.py +0 -0
  27. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/config.py +0 -0
  28. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/context.py +0 -0
  29. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/io.py +0 -0
  30. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/main.py +0 -0
  31. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/mlflow.py +0 -0
  32. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/core/param.py +0 -0
  33. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/entities/__init__.py +0 -0
  34. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/entities/run_collection.py +0 -0
  35. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/entities/run_data.py +0 -0
  36. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/entities/run_info.py +0 -0
  37. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/executor/__init__.py +0 -0
  38. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/executor/conf.py +0 -0
  39. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/executor/parser.py +0 -0
  40. {hydraflow-0.10.1 → hydraflow-0.10.2}/src/hydraflow/py.typed +0 -0
  41. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/__init__.py +0 -0
  42. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/__init__.py +0 -0
  43. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/app.py +0 -0
  44. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/conftest.py +0 -0
  45. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/hydraflow.yaml +0 -0
  46. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/test_setup.py +0 -0
  47. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/cli/test_version.py +0 -0
  48. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/conftest.py +0 -0
  49. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/__init__.py +0 -0
  50. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/config/__init__.py +0 -0
  51. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/config/test_config.py +0 -0
  52. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/config/test_params.py +0 -0
  53. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/__init__.py +0 -0
  54. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/chdir.py +0 -0
  55. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/log_run.py +0 -0
  56. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/start_run.py +0 -0
  57. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/test_chdir.py +0 -0
  58. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/test_log_run.py +0 -0
  59. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/context/test_start_run.py +0 -0
  60. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/io/__init__.py +0 -0
  61. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/io/hydra_dir.py +0 -0
  62. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/io/test_hydra_dir.py +0 -0
  63. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/io/test_iter_dirs.py +0 -0
  64. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/io/test_run.py +0 -0
  65. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/__init__.py +0 -0
  66. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/default.py +0 -0
  67. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/force_new_run.py +0 -0
  68. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/match_overrides.py +0 -0
  69. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/rerun_finished.py +0 -0
  70. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/skip_finished.py +0 -0
  71. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/test_default.py +0 -0
  72. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/test_force_new_run.py +0 -0
  73. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/test_match_overrides.py +0 -0
  74. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/test_rerun_finished.py +0 -0
  75. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/main/test_skip_finished.py +0 -0
  76. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/param/__init__.py +0 -0
  77. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/param/params.py +0 -0
  78. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/param/test_param.py +0 -0
  79. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/param/test_params.py +0 -0
  80. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/core/test_mlflow.py +0 -0
  81. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/__init__.py +0 -0
  82. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/filter.py +0 -0
  83. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/test_collection.py +0 -0
  84. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/test_data.py +0 -0
  85. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/test_filter.py +0 -0
  86. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/test_info.py +0 -0
  87. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/test_values.py +0 -0
  88. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/entities/values.py +0 -0
  89. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/__init__.py +0 -0
  90. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/conftest.py +0 -0
  91. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/echo.py +0 -0
  92. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/test_args.py +0 -0
  93. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/test_conf.py +0 -0
  94. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/test_io.py +0 -0
  95. {hydraflow-0.10.1 → hydraflow-0.10.2}/tests/executor/test_parser.py +0 -0
@@ -1,5 +1,5 @@
1
1
  *.db
2
- .coverage
2
+ .coverage*
3
3
  .env
4
4
  .venv/
5
5
  __pycache__/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.10.1
3
+ Version: 0.10.2
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hydraflow"
7
- version = "0.10.1"
7
+ version = "0.10.2"
8
8
  description = "Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -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)
@@ -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
@@ -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
 
@@ -99,6 +100,8 @@ def multirun(job: Job) -> None:
99
100
 
100
101
  if job.run:
101
102
  base_cmds = shlex.split(job.run)
103
+ if base_cmds[0] == "python" and sys.platform == "win32":
104
+ base_cmds[0] = sys.executable
102
105
 
103
106
  for args in it:
104
107
  cmds = [*base_cmds, *args]
@@ -132,24 +135,31 @@ def multirun(job: Job) -> None:
132
135
  raise RuntimeError(msg) from e
133
136
 
134
137
 
135
- def show(job: Job) -> None:
136
- """Show the job configuration.
138
+ def to_text(job: Job) -> str:
139
+ """Convert the job configuration to a string.
137
140
 
138
- This function shows the job configuration for a given job.
141
+ This function returns the job configuration for a given job.
139
142
 
140
143
  Args:
141
144
  job (Job): The job configuration to show.
142
145
 
146
+ Returns:
147
+ str: The job configuration.
148
+
143
149
  """
150
+ text = ""
151
+
144
152
  it = iter_batches(job)
145
153
 
146
154
  if job.run:
147
155
  base_cmds = shlex.split(job.run)
148
156
  for args in it:
149
157
  cmds = " ".join([*base_cmds, *args])
150
- print(cmds) # noqa: T201
158
+ text += f"{cmds}\n"
151
159
 
152
160
  elif job.call:
153
- print(f"call: {job.call}") # noqa: T201
161
+ text = f"call: {job.call}\n"
154
162
  for args in it:
155
- print(f"args: {args}") # noqa: T201
163
+ text += f"args: {args}\n"
164
+
165
+ return text
@@ -0,0 +1,41 @@
1
+ import pytest
2
+ from typer.testing import CliRunner
3
+
4
+ import hydraflow
5
+ from hydraflow.cli import app
6
+
7
+ pytestmark = pytest.mark.xdist_group(name="group1")
8
+
9
+ runner = CliRunner()
10
+
11
+
12
+ def test_run_args():
13
+ result = runner.invoke(app, ["run", "args"])
14
+ assert result.exit_code == 0
15
+ run_ids = hydraflow.list_run_ids("args")
16
+ assert len(run_ids) == 12
17
+
18
+
19
+ def test_run_batch():
20
+ result = runner.invoke(app, ["run", "batch"])
21
+ assert result.exit_code == 0
22
+ run_ids = hydraflow.list_run_ids("batch")
23
+ assert len(run_ids) == 8
24
+
25
+
26
+ def test_run_args_dry_run():
27
+ result = runner.invoke(app, ["run", "args", "--dry-run"])
28
+ assert result.exit_code == 0
29
+ assert "hydra.job.name=args" in result.stdout
30
+ assert "count=1,2,3 name=a,b" in result.stdout
31
+ assert "count=4,5,6 name=c,d" in result.stdout
32
+
33
+
34
+ def test_run_batch_dry_run():
35
+ result = runner.invoke(app, ["run", "batch", "--dry-run"])
36
+ assert result.exit_code == 0
37
+ assert "hydra.job.name=batch" in result.stdout
38
+ assert "count=1,2 name=a" in result.stdout
39
+ assert "count=1,2 name=b" in result.stdout
40
+ assert "count=100 name=c,d" in result.stdout
41
+ assert "count=100 name=e,f" in result.stdout
@@ -0,0 +1,18 @@
1
+ from typer.testing import CliRunner
2
+
3
+ from hydraflow.cli import app
4
+
5
+ runner = CliRunner()
6
+
7
+
8
+ def test_show():
9
+ result = runner.invoke(app, ["show"])
10
+ assert result.exit_code == 0
11
+ assert "jobs:\n" in result.stdout
12
+ assert " args:\n" in result.stdout
13
+
14
+
15
+ def test_show_job():
16
+ result = runner.invoke(app, ["show", "args"])
17
+ assert result.exit_code == 0
18
+ assert "name: args\n" in result.stdout
@@ -71,17 +71,13 @@ def test_sweep_args(batches, i, x):
71
71
  assert batches[i][-2] == x
72
72
 
73
73
 
74
- @pytest.mark.skipif(
75
- sys.platform == "win32",
76
- reason="Windows does not support this test",
77
- )
78
74
  def test_multirun_run(job: Job, tmp_path: Path):
79
75
  from hydraflow.executor.job import multirun
80
76
 
81
77
  path = tmp_path / "output.txt"
82
78
  file = Path(__file__).parent / "echo.py"
83
79
 
84
- job.run = f"{sys.executable} {file.as_posix()} {path.as_posix()}"
80
+ job.run = f"python {file.as_posix()} {path.as_posix()}"
85
81
  multirun(job)
86
82
  assert path.read_text() == "a=1,2 b=5 a=1,2 b=6 a=3,4 c=7 a=3,4 c=8"
87
83
 
@@ -89,7 +85,7 @@ def test_multirun_run(job: Job, tmp_path: Path):
89
85
  def test_multirun_run_error(job: Job):
90
86
  from hydraflow.executor.job import multirun
91
87
 
92
- job.run = "false"
88
+ job.run = "cmd /c exit 1" if sys.platform == "win32" else "false"
93
89
  with pytest.raises(RuntimeError):
94
90
  multirun(job)
95
91
 
@@ -137,11 +133,10 @@ def test_multirun_call_not_found(job: Job):
137
133
  multirun(job)
138
134
 
139
135
 
140
- def test_show(job: Job, capsys):
141
- from hydraflow.executor.job import show
136
+ def test_to_text(job: Job):
137
+ from hydraflow.executor.job import to_text
142
138
 
143
139
  job.call = "typer.echo"
144
- show(job)
145
- out, _ = capsys.readouterr()
146
- assert "call: typer.echo\n" in out
147
- assert "'hydra.job.name=test', 'a=3,4', 'c=8']" in out
140
+ text = to_text(job)
141
+ assert "call: typer.echo\n" in text
142
+ assert "'hydra.job.name=test', 'a=3,4', 'c=8']" in text
@@ -1,33 +0,0 @@
1
- import sys
2
-
3
- import pytest
4
- from typer.testing import CliRunner
5
-
6
- import hydraflow
7
- from hydraflow.cli import app
8
-
9
- pytestmark = pytest.mark.xdist_group(name="group1")
10
-
11
- runner = CliRunner()
12
-
13
-
14
- @pytest.mark.skipif(
15
- sys.platform == "win32",
16
- reason="Windows does not support this test",
17
- )
18
- def test_run_args():
19
- result = runner.invoke(app, ["run", "args"])
20
- assert result.exit_code == 0
21
- run_ids = hydraflow.list_run_ids("args")
22
- assert len(run_ids) == 12
23
-
24
-
25
- @pytest.mark.skipif(
26
- sys.platform == "win32",
27
- reason="Windows does not support this test",
28
- )
29
- def test_run_batch():
30
- result = runner.invoke(app, ["run", "batch"])
31
- assert result.exit_code == 0
32
- run_ids = hydraflow.list_run_ids("batch")
33
- assert len(run_ids) == 8
@@ -1,23 +0,0 @@
1
- from typer.testing import CliRunner
2
-
3
- from hydraflow.cli import app
4
-
5
- runner = CliRunner()
6
-
7
-
8
- def test_show_args():
9
- result = runner.invoke(app, ["show", "args"])
10
- assert result.exit_code == 0
11
- assert "hydra.job.name=args" in result.stdout
12
- assert "count=1,2,3 name=a,b" in result.stdout
13
- assert "count=4,5,6 name=c,d" in result.stdout
14
-
15
-
16
- def test_show_batch():
17
- result = runner.invoke(app, ["show", "batch"])
18
- assert result.exit_code == 0
19
- assert "hydra.job.name=batch" in result.stdout
20
- assert "count=1,2 name=a" in result.stdout
21
- assert "count=1,2 name=b" in result.stdout
22
- assert "count=100 name=c,d" in result.stdout
23
- assert "count=100 name=e,f" in result.stdout
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes