hydraflow 0.9.0__tar.gz → 0.9.1__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 (92) hide show
  1. {hydraflow-0.9.0 → hydraflow-0.9.1}/PKG-INFO +1 -1
  2. {hydraflow-0.9.0 → hydraflow-0.9.1}/pyproject.toml +1 -1
  3. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/__init__.py +8 -0
  4. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/io.py +72 -1
  5. hydraflow-0.9.1/tests/core/io/test_iter_dirs.py +82 -0
  6. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/test_mlflow.py +7 -0
  7. {hydraflow-0.9.0 → hydraflow-0.9.1}/.devcontainer/devcontainer.json +0 -0
  8. {hydraflow-0.9.0 → hydraflow-0.9.1}/.devcontainer/postCreate.sh +0 -0
  9. {hydraflow-0.9.0 → hydraflow-0.9.1}/.devcontainer/starship.toml +0 -0
  10. {hydraflow-0.9.0 → hydraflow-0.9.1}/.gitattributes +0 -0
  11. {hydraflow-0.9.0 → hydraflow-0.9.1}/.github/workflows/ci.yaml +0 -0
  12. {hydraflow-0.9.0 → hydraflow-0.9.1}/.github/workflows/docs.yaml +0 -0
  13. {hydraflow-0.9.0 → hydraflow-0.9.1}/.gitignore +0 -0
  14. {hydraflow-0.9.0 → hydraflow-0.9.1}/LICENSE +0 -0
  15. {hydraflow-0.9.0 → hydraflow-0.9.1}/README.md +0 -0
  16. {hydraflow-0.9.0 → hydraflow-0.9.1}/apps/quickstart.py +0 -0
  17. {hydraflow-0.9.0 → hydraflow-0.9.1}/docs/index.md +0 -0
  18. {hydraflow-0.9.0 → hydraflow-0.9.1}/docs/usage/quickstart.md +0 -0
  19. {hydraflow-0.9.0 → hydraflow-0.9.1}/mkdocs.yaml +0 -0
  20. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/cli.py +0 -0
  21. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/__init__.py +0 -0
  22. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/config.py +0 -0
  23. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/context.py +0 -0
  24. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/main.py +0 -0
  25. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/mlflow.py +0 -0
  26. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/core/param.py +0 -0
  27. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/entities/__init__.py +0 -0
  28. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/entities/run_collection.py +0 -0
  29. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/entities/run_data.py +0 -0
  30. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/entities/run_info.py +0 -0
  31. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/executor/__init__.py +0 -0
  32. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/executor/conf.py +0 -0
  33. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/executor/io.py +0 -0
  34. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/executor/job.py +0 -0
  35. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/executor/parser.py +0 -0
  36. {hydraflow-0.9.0 → hydraflow-0.9.1}/src/hydraflow/py.typed +0 -0
  37. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/__init__.py +0 -0
  38. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/__init__.py +0 -0
  39. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/app.py +0 -0
  40. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/conftest.py +0 -0
  41. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/hydraflow.yaml +0 -0
  42. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/test_run.py +0 -0
  43. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/test_setup.py +0 -0
  44. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/test_show.py +0 -0
  45. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/cli/test_version.py +0 -0
  46. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/conftest.py +0 -0
  47. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/__init__.py +0 -0
  48. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/config/__init__.py +0 -0
  49. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/config/test_config.py +0 -0
  50. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/config/test_params.py +0 -0
  51. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/__init__.py +0 -0
  52. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/chdir.py +0 -0
  53. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/log_run.py +0 -0
  54. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/start_run.py +0 -0
  55. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/test_chdir.py +0 -0
  56. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/test_log_run.py +0 -0
  57. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/context/test_start_run.py +0 -0
  58. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/io/__init__.py +0 -0
  59. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/io/hydra_dir.py +0 -0
  60. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/io/test_hydra_dir.py +0 -0
  61. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/io/test_run.py +0 -0
  62. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/__init__.py +0 -0
  63. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/default.py +0 -0
  64. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/force_new_run.py +0 -0
  65. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/match_overrides.py +0 -0
  66. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/rerun_finished.py +0 -0
  67. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/skip_finished.py +0 -0
  68. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/test_default.py +0 -0
  69. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/test_force_new_run.py +0 -0
  70. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/test_match_overrides.py +0 -0
  71. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/test_rerun_finished.py +0 -0
  72. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/main/test_skip_finished.py +0 -0
  73. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/param/__init__.py +0 -0
  74. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/param/params.py +0 -0
  75. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/param/test_param.py +0 -0
  76. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/core/param/test_params.py +0 -0
  77. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/__init__.py +0 -0
  78. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/filter.py +0 -0
  79. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/test_collection.py +0 -0
  80. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/test_data.py +0 -0
  81. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/test_filter.py +0 -0
  82. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/test_info.py +0 -0
  83. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/test_values.py +0 -0
  84. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/entities/values.py +0 -0
  85. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/__init__.py +0 -0
  86. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/conftest.py +0 -0
  87. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/echo.py +0 -0
  88. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/test_args.py +0 -0
  89. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/test_conf.py +0 -0
  90. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/test_io.py +0 -0
  91. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/test_job.py +0 -0
  92. {hydraflow-0.9.0 → hydraflow-0.9.1}/tests/executor/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.9.0
3
+ Version: 0.9.1
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.9.0"
7
+ version = "0.9.1"
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" }
@@ -5,6 +5,10 @@ from hydraflow.core.io import (
5
5
  get_artifact_dir,
6
6
  get_artifact_path,
7
7
  get_hydra_output_dir,
8
+ iter_artifact_paths,
9
+ iter_artifacts_dirs,
10
+ iter_experiment_dirs,
11
+ iter_run_dirs,
8
12
  load_config,
9
13
  remove_run,
10
14
  )
@@ -18,6 +22,10 @@ __all__ = [
18
22
  "get_artifact_dir",
19
23
  "get_artifact_path",
20
24
  "get_hydra_output_dir",
25
+ "iter_artifact_paths",
26
+ "iter_artifacts_dirs",
27
+ "iter_experiment_dirs",
28
+ "iter_run_dirs",
21
29
  "list_run_ids",
22
30
  "list_run_paths",
23
31
  "list_runs",
@@ -15,7 +15,7 @@ from mlflow.entities import Run
15
15
  from omegaconf import DictConfig, ListConfig, OmegaConf
16
16
 
17
17
  if TYPE_CHECKING:
18
- from collections.abc import Iterable
18
+ from collections.abc import Iterable, Iterator
19
19
 
20
20
 
21
21
  def file_uri_to_path(uri: str) -> Path:
@@ -147,3 +147,74 @@ def remove_run(run: Run | Iterable[Run]) -> None:
147
147
  return
148
148
 
149
149
  shutil.rmtree(get_artifact_dir(run).parent)
150
+
151
+
152
+ def get_root_dir(uri: str | Path | None = None) -> Path:
153
+ """Get the root directory for the MLflow tracking server."""
154
+ if uri is not None:
155
+ return Path(uri).absolute()
156
+
157
+ uri = mlflow.get_tracking_uri()
158
+
159
+ if uri.startswith("file:"):
160
+ return file_uri_to_path(uri)
161
+
162
+ return Path(uri).absolute()
163
+
164
+
165
+ def get_experiment_name(path: Path) -> str | None:
166
+ """Get the experiment name from the meta file."""
167
+ metafile = path / "meta.yaml"
168
+ if not metafile.exists():
169
+ return None
170
+ lines = metafile.read_text().splitlines()
171
+ for line in lines:
172
+ if line.startswith("name:"):
173
+ return line.split(":")[1].strip()
174
+ return None
175
+
176
+
177
+ def iter_experiment_dirs(
178
+ experiment_names: str | list[str] | None = None,
179
+ root_dir: str | Path | None = None,
180
+ ) -> Iterator[Path]:
181
+ """Iterate over the experiment directories in the root directory."""
182
+ if isinstance(experiment_names, str):
183
+ experiment_names = [experiment_names]
184
+
185
+ root_dir = get_root_dir(root_dir)
186
+ for path in root_dir.iterdir():
187
+ if path.is_dir() and path.name not in [".trash", "0"]:
188
+ if name := get_experiment_name(path):
189
+ if experiment_names is None or name in experiment_names:
190
+ yield path
191
+
192
+
193
+ def iter_run_dirs(
194
+ experiment_names: str | list[str] | None = None,
195
+ root_dir: str | Path | None = None,
196
+ ) -> Iterator[Path]:
197
+ """Iterate over the run directories in the root directory."""
198
+ for experiment_dir in iter_experiment_dirs(experiment_names, root_dir):
199
+ for path in experiment_dir.iterdir():
200
+ if path.is_dir() and (path / "artifacts").exists():
201
+ yield path
202
+
203
+
204
+ def iter_artifacts_dirs(
205
+ experiment_names: str | list[str] | None = None,
206
+ root_dir: str | Path | None = None,
207
+ ) -> Iterator[Path]:
208
+ """Iterate over the artifacts directories in the root directory."""
209
+ for path in iter_run_dirs(experiment_names, root_dir):
210
+ yield path / "artifacts"
211
+
212
+
213
+ def iter_artifact_paths(
214
+ artifact_path: str | Path,
215
+ experiment_names: str | list[str] | None = None,
216
+ root_dir: str | Path | None = None,
217
+ ) -> Iterator[Path]:
218
+ """Iterate over the artifact paths in the root directory."""
219
+ for path in iter_artifacts_dirs(experiment_names, root_dir):
220
+ yield path / artifact_path
@@ -0,0 +1,82 @@
1
+ from pathlib import Path
2
+
3
+ import mlflow
4
+ import pytest
5
+
6
+ pytestmark = pytest.mark.xdist_group(name="group1")
7
+
8
+
9
+ @pytest.fixture(scope="module")
10
+ def root_dir(chdir):
11
+ return Path("mlruns").absolute()
12
+
13
+
14
+ @pytest.fixture(scope="module", autouse=True)
15
+ def setup(chdir):
16
+ mlflow.set_experiment("e1")
17
+ with mlflow.start_run():
18
+ mlflow.log_text("1", "text.txt")
19
+ with mlflow.start_run():
20
+ mlflow.log_text("2", "text.txt")
21
+ mlflow.set_experiment("e2")
22
+ with mlflow.start_run():
23
+ mlflow.log_text("3", "text.txt")
24
+ with mlflow.start_run():
25
+ mlflow.log_text("4", "text.txt")
26
+ with mlflow.start_run():
27
+ mlflow.log_text("5", "text.txt")
28
+
29
+
30
+ def test_root_dir(root_dir: Path):
31
+ from hydraflow.core.io import get_root_dir
32
+
33
+ assert get_root_dir(root_dir) == root_dir
34
+ assert get_root_dir(root_dir.name) == root_dir
35
+ assert get_root_dir() == root_dir
36
+
37
+
38
+ def test_iter_experiment_dirs():
39
+ from hydraflow.core.io import get_experiment_name, iter_experiment_dirs
40
+
41
+ names = [get_experiment_name(p) for p in iter_experiment_dirs()]
42
+ assert sorted(names) == ["e1", "e2"] # type: ignore
43
+
44
+
45
+ def test_iter_experiment_dirs_filter():
46
+ from hydraflow.core.io import get_experiment_name, iter_experiment_dirs
47
+
48
+ it = iter_experiment_dirs(experiment_names="e1")
49
+ assert [get_experiment_name(p) for p in it] == ["e1"]
50
+
51
+
52
+ def test_get_experiment_name_none(root_dir: Path):
53
+ from hydraflow.core.io import get_experiment_name
54
+
55
+ assert get_experiment_name(root_dir.parent) is None
56
+
57
+
58
+ def test_get_experiment_name_metafile_none(root_dir: Path):
59
+ from hydraflow.core.io import get_experiment_name
60
+
61
+ (root_dir / "meta.yaml").touch()
62
+ assert get_experiment_name(root_dir) is None
63
+
64
+
65
+ def test_iter_run_dirs():
66
+ from hydraflow.core.io import iter_run_dirs
67
+
68
+ assert len(list(iter_run_dirs())) == 5
69
+
70
+
71
+ def test_iter_artifacts_dirs():
72
+ from hydraflow.core.io import iter_artifacts_dirs
73
+
74
+ assert len(list(iter_artifacts_dirs())) == 5
75
+
76
+
77
+ def test_iter_artifact_paths():
78
+ from hydraflow.core.io import iter_artifact_paths
79
+
80
+ it = iter_artifact_paths("text.txt")
81
+ text = sorted("".join(p.read_text() for p in it))
82
+ assert text == ["1", "2", "3", "4", "5"]
@@ -81,3 +81,10 @@ def test_list_run_paths(experiment: Experiment):
81
81
 
82
82
  dirs = list_run_paths(experiment.name, "artifacts")
83
83
  assert all(d.is_dir() for d in dirs)
84
+
85
+
86
+ @pytest.mark.parametrize("uri", [None, "test_mlflow"])
87
+ def test_root_dir(experiment, uri):
88
+ from hydraflow.core.io import get_root_dir
89
+
90
+ assert get_root_dir(uri) == Path.cwd() / "test_mlflow"
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
File without changes