hydraflow 0.7.3__py3-none-any.whl → 0.7.5__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- hydraflow/__init__.py +9 -1
- hydraflow/cli.py +75 -0
- hydraflow/mlflow.py +87 -32
- {hydraflow-0.7.3.dist-info → hydraflow-0.7.5.dist-info}/METADATA +5 -2
- {hydraflow-0.7.3.dist-info → hydraflow-0.7.5.dist-info}/RECORD +8 -6
- hydraflow-0.7.5.dist-info/entry_points.txt +2 -0
- {hydraflow-0.7.3.dist-info → hydraflow-0.7.5.dist-info}/WHEEL +0 -0
- {hydraflow-0.7.3.dist-info → hydraflow-0.7.5.dist-info}/licenses/LICENSE +0 -0
hydraflow/__init__.py
CHANGED
@@ -3,7 +3,13 @@
|
|
3
3
|
from hydraflow.config import select_config, select_overrides
|
4
4
|
from hydraflow.context import chdir_artifact, log_run, start_run
|
5
5
|
from hydraflow.main import main
|
6
|
-
from hydraflow.mlflow import
|
6
|
+
from hydraflow.mlflow import (
|
7
|
+
list_run_ids,
|
8
|
+
list_run_paths,
|
9
|
+
list_runs,
|
10
|
+
search_runs,
|
11
|
+
set_experiment,
|
12
|
+
)
|
7
13
|
from hydraflow.run_collection import RunCollection
|
8
14
|
from hydraflow.utils import (
|
9
15
|
get_artifact_dir,
|
@@ -22,6 +28,8 @@ __all__ = [
|
|
22
28
|
"get_artifact_path",
|
23
29
|
"get_hydra_output_dir",
|
24
30
|
"get_overrides",
|
31
|
+
"list_run_ids",
|
32
|
+
"list_run_paths",
|
25
33
|
"list_runs",
|
26
34
|
"load_config",
|
27
35
|
"load_overrides",
|
hydraflow/cli.py
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
"""Hydraflow CLI."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Annotated
|
7
|
+
|
8
|
+
import typer
|
9
|
+
from omegaconf import DictConfig, OmegaConf
|
10
|
+
from rich.console import Console
|
11
|
+
from typer import Argument, Option
|
12
|
+
|
13
|
+
app = typer.Typer(add_completion=False)
|
14
|
+
console = Console()
|
15
|
+
|
16
|
+
|
17
|
+
@app.command()
|
18
|
+
def run(
|
19
|
+
names: Annotated[
|
20
|
+
list[str] | None,
|
21
|
+
Argument(help="Job names.", show_default=False),
|
22
|
+
] = None,
|
23
|
+
) -> None:
|
24
|
+
"""Run jobs."""
|
25
|
+
typer.echo(names)
|
26
|
+
|
27
|
+
cfg = load_config()
|
28
|
+
typer.echo(cfg)
|
29
|
+
|
30
|
+
|
31
|
+
@app.command()
|
32
|
+
def show() -> None:
|
33
|
+
"""Show the config."""
|
34
|
+
from rich.syntax import Syntax
|
35
|
+
|
36
|
+
cfg = load_config()
|
37
|
+
code = OmegaConf.to_yaml(cfg)
|
38
|
+
syntax = Syntax(code, "yaml")
|
39
|
+
console.print(syntax)
|
40
|
+
|
41
|
+
|
42
|
+
@app.callback(invoke_without_command=True)
|
43
|
+
def callback(
|
44
|
+
*,
|
45
|
+
version: Annotated[
|
46
|
+
bool,
|
47
|
+
Option("--version", help="Show the version and exit."),
|
48
|
+
] = False,
|
49
|
+
) -> None:
|
50
|
+
if version:
|
51
|
+
import importlib.metadata
|
52
|
+
|
53
|
+
typer.echo(f"hydraflow {importlib.metadata.version('hydraflow')}")
|
54
|
+
raise typer.Exit
|
55
|
+
|
56
|
+
|
57
|
+
def find_config() -> Path:
|
58
|
+
if Path("hydraflow.yaml").exists():
|
59
|
+
return Path("hydraflow.yaml")
|
60
|
+
|
61
|
+
if Path("hydraflow.yml").exists():
|
62
|
+
return Path("hydraflow.yml")
|
63
|
+
|
64
|
+
typer.echo("No config file found.")
|
65
|
+
raise typer.Exit(code=1)
|
66
|
+
|
67
|
+
|
68
|
+
def load_config() -> DictConfig:
|
69
|
+
cfg = OmegaConf.load(find_config())
|
70
|
+
|
71
|
+
if isinstance(cfg, DictConfig):
|
72
|
+
return cfg
|
73
|
+
|
74
|
+
typer.echo("Invalid config file.")
|
75
|
+
raise typer.Exit(code=1)
|
hydraflow/mlflow.py
CHANGED
@@ -153,18 +153,17 @@ def search_runs( # noqa: PLR0913
|
|
153
153
|
return RunCollection(runs) # type: ignore
|
154
154
|
|
155
155
|
|
156
|
-
def
|
156
|
+
def list_run_paths(
|
157
157
|
experiment_names: str | list[str] | None = None,
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
"""List all runs for the specified experiments.
|
158
|
+
*other: str,
|
159
|
+
) -> list[Path]:
|
160
|
+
"""List all run paths for the specified experiments.
|
162
161
|
|
163
|
-
This function retrieves all
|
162
|
+
This function retrieves all run paths for the given list of experiment names.
|
164
163
|
If no experiment names are provided (None), it defaults to searching all runs
|
165
164
|
for the currently active experiment. If an empty list is provided, the function
|
166
165
|
will search all runs for all experiments except the "Default" experiment.
|
167
|
-
The function returns the results as a `
|
166
|
+
The function returns the results as a list of `Path` objects.
|
168
167
|
|
169
168
|
Note:
|
170
169
|
The returned runs are sorted by their start time in ascending order.
|
@@ -174,27 +173,12 @@ def list_runs(
|
|
174
173
|
for runs. If None or an empty list is provided, the function will
|
175
174
|
search the currently active experiment or all experiments except
|
176
175
|
the "Default" experiment.
|
177
|
-
|
178
|
-
will search runs sequentially.
|
179
|
-
status (str | list[str] | int | list[int] | None): The status of the runs
|
180
|
-
to filter.
|
176
|
+
other (str): The parts of the run directory to join.
|
181
177
|
|
182
178
|
Returns:
|
183
|
-
|
184
|
-
specified experiments.
|
179
|
+
list[Path]: A list of run paths for the specified experiments.
|
185
180
|
|
186
181
|
"""
|
187
|
-
rc = _list_runs(experiment_names, n_jobs)
|
188
|
-
if status is None:
|
189
|
-
return rc
|
190
|
-
|
191
|
-
return rc.filter(status=status)
|
192
|
-
|
193
|
-
|
194
|
-
def _list_runs(
|
195
|
-
experiment_names: str | list[str] | None = None,
|
196
|
-
n_jobs: int = 0,
|
197
|
-
) -> RunCollection:
|
198
182
|
if isinstance(experiment_names, str):
|
199
183
|
experiment_names = [experiment_names]
|
200
184
|
|
@@ -202,14 +186,11 @@ def _list_runs(
|
|
202
186
|
experiments = mlflow.search_experiments()
|
203
187
|
experiment_names = [e.name for e in experiments if e.name != "Default"]
|
204
188
|
|
205
|
-
if n_jobs == 0:
|
206
|
-
return search_runs(experiment_names=experiment_names)
|
207
|
-
|
208
189
|
if experiment_names is None:
|
209
190
|
experiment_id = _get_experiment_id()
|
210
191
|
experiment_names = [mlflow.get_experiment(experiment_id).name]
|
211
192
|
|
212
|
-
|
193
|
+
run_paths: list[Path] = []
|
213
194
|
|
214
195
|
for name in experiment_names:
|
215
196
|
if experiment := mlflow.get_experiment_by_name(name):
|
@@ -217,9 +198,83 @@ def _list_runs(
|
|
217
198
|
|
218
199
|
if isinstance(uri, str):
|
219
200
|
path = get_artifact_dir(uri=uri)
|
220
|
-
|
201
|
+
run_paths.extend(p for p in path.iterdir() if p.is_dir())
|
202
|
+
|
203
|
+
if other:
|
204
|
+
return [p.joinpath(*other) for p in run_paths]
|
205
|
+
|
206
|
+
return run_paths
|
207
|
+
|
208
|
+
|
209
|
+
def list_run_ids(experiment_names: str | list[str] | None = None) -> list[str]:
|
210
|
+
"""List all run IDs for the specified experiments.
|
211
|
+
|
212
|
+
This function retrieves all runs for the given list of experiment names.
|
213
|
+
If no experiment names are provided (None), it defaults to searching all runs
|
214
|
+
for the currently active experiment. If an empty list is provided, the function
|
215
|
+
will search all runs for all experiments except the "Default" experiment.
|
216
|
+
The function returns the results as a list of string.
|
217
|
+
|
218
|
+
Note:
|
219
|
+
The returned runs are sorted by their start time in ascending order.
|
220
|
+
|
221
|
+
Args:
|
222
|
+
experiment_names (list[str] | None): List of experiment names to search
|
223
|
+
for runs. If None or an empty list is provided, the function will
|
224
|
+
search the currently active experiment or all experiments except
|
225
|
+
the "Default" experiment.
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
list[str]: A list of run IDs for the specified experiments.
|
229
|
+
|
230
|
+
"""
|
231
|
+
return [run_dir.stem for run_dir in list_run_paths(experiment_names)]
|
232
|
+
|
233
|
+
|
234
|
+
def list_runs(
|
235
|
+
experiment_names: str | list[str] | None = None,
|
236
|
+
n_jobs: int = 0,
|
237
|
+
status: str | list[str] | int | list[int] | None = None,
|
238
|
+
) -> RunCollection:
|
239
|
+
"""List all runs for the specified experiments.
|
240
|
+
|
241
|
+
This function retrieves all runs for the given list of experiment names.
|
242
|
+
If no experiment names are provided (None), it defaults to searching all runs
|
243
|
+
for the currently active experiment. If an empty list is provided, the function
|
244
|
+
will search all runs for all experiments except the "Default" experiment.
|
245
|
+
The function returns the results as a `RunCollection` object.
|
246
|
+
|
247
|
+
Note:
|
248
|
+
The returned runs are sorted by their start time in ascending order.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
experiment_names (list[str] | None): List of experiment names to search
|
252
|
+
for runs. If None or an empty list is provided, the function will
|
253
|
+
search the currently active experiment or all experiments except
|
254
|
+
the "Default" experiment.
|
255
|
+
n_jobs (int): The number of jobs to run in parallel. If 0, the function
|
256
|
+
will search runs sequentially.
|
257
|
+
status (str | list[str] | int | list[int] | None): The status of the runs
|
258
|
+
to filter.
|
259
|
+
|
260
|
+
Returns:
|
261
|
+
RunCollection: A `RunCollection` instance containing the runs for the
|
262
|
+
specified experiments.
|
263
|
+
|
264
|
+
"""
|
265
|
+
run_ids = list_run_ids(experiment_names)
|
266
|
+
|
267
|
+
if n_jobs == 0:
|
268
|
+
runs = [mlflow.get_run(run_id) for run_id in run_ids]
|
269
|
+
|
270
|
+
else:
|
271
|
+
it = (joblib.delayed(mlflow.get_run)(run_id) for run_id in run_ids)
|
272
|
+
runs = joblib.Parallel(n_jobs, prefer="threads")(it)
|
221
273
|
|
222
|
-
it = (joblib.delayed(mlflow.get_run)(run_id) for run_id in run_ids)
|
223
|
-
runs = joblib.Parallel(n_jobs, prefer="threads")(it)
|
224
274
|
runs = sorted(runs, key=lambda run: run.info.start_time) # type: ignore
|
225
|
-
|
275
|
+
rc = RunCollection(runs) # type: ignore
|
276
|
+
|
277
|
+
if status is None:
|
278
|
+
return rc
|
279
|
+
|
280
|
+
return rc.filter(status=status)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hydraflow
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.5
|
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
|
@@ -38,6 +38,9 @@ Classifier: Programming Language :: Python :: 3.13
|
|
38
38
|
Requires-Python: >=3.10
|
39
39
|
Requires-Dist: hydra-core>=1.3
|
40
40
|
Requires-Dist: mlflow>=2.15
|
41
|
+
Requires-Dist: omegaconf
|
42
|
+
Requires-Dist: rich
|
43
|
+
Requires-Dist: typer
|
41
44
|
Description-Content-Type: text/markdown
|
42
45
|
|
43
46
|
# Hydraflow
|
@@ -52,7 +55,7 @@ Description-Content-Type: text/markdown
|
|
52
55
|
[pypi-v-link]: https://pypi.org/project/hydraflow/
|
53
56
|
[python-v-image]: https://img.shields.io/pypi/pyversions/hydraflow.svg
|
54
57
|
[python-v-link]: https://pypi.org/project/hydraflow
|
55
|
-
[GHAction-image]: https://github.com/daizutabi/hydraflow/actions/workflows/ci.
|
58
|
+
[GHAction-image]: https://github.com/daizutabi/hydraflow/actions/workflows/ci.yaml/badge.svg?branch=main&event=push
|
56
59
|
[GHAction-link]: https://github.com/daizutabi/hydraflow/actions?query=event%3Apush+branch%3Amain
|
57
60
|
[codecov-image]: https://codecov.io/github/daizutabi/hydraflow/coverage.svg?branch=main
|
58
61
|
[codecov-link]: https://codecov.io/github/daizutabi/hydraflow?branch=main
|
@@ -1,15 +1,17 @@
|
|
1
|
-
hydraflow/__init__.py,sha256=
|
1
|
+
hydraflow/__init__.py,sha256=0HJOiiKhfH3MFbuoL_BLaBaruVSb53Scimt2_2rRI28,995
|
2
|
+
hydraflow/cli.py,sha256=jxqFppNeJWAr2Tb-C_MQXEJtegJ6TXcd3C1CT7Jdb1A,1559
|
2
3
|
hydraflow/config.py,sha256=MNX9da5bPVDcjnpji7Cm9ndK6ura92pt361m4PRh6_E,4326
|
3
4
|
hydraflow/context.py,sha256=3xfKhMozkKFqtWeOp9Gie0A5o5URMta4US6iVD5TcLU,6002
|
4
5
|
hydraflow/main.py,sha256=hroncI_SNpNgEtdxLgzI397J5S2Amv7J0atnPxwBePM,1314
|
5
|
-
hydraflow/mlflow.py,sha256=
|
6
|
+
hydraflow/mlflow.py,sha256=lKpY5tPJRXXlvT5ZFVz1kROHsuvzGhp5kp8RiT2jlX8,10912
|
6
7
|
hydraflow/param.py,sha256=yu1aMNXRLegXGDL-68vwIkfeDF9CaU784WZENGLwl7Q,4572
|
7
8
|
hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
9
|
hydraflow/run_collection.py,sha256=YCWg5Dz1j49xB2LA75onq5wsAeQQbifXpG4yPUwRN4I,24776
|
9
10
|
hydraflow/run_data.py,sha256=dpyyfnuH9mCtIZeigMo1iFQo9bafMdEL4i4uI2l0UqY,1525
|
10
11
|
hydraflow/run_info.py,sha256=Jf5wrIjRLIV1-k-obHDqwKHa6j_ZonrY8od-rXlbtMo,1024
|
11
12
|
hydraflow/utils.py,sha256=a9i5PEJn8Ssowv9dqHadAihZXlsqtVjHZ9MZvkPq1bY,4747
|
12
|
-
hydraflow-0.7.
|
13
|
-
hydraflow-0.7.
|
14
|
-
hydraflow-0.7.
|
15
|
-
hydraflow-0.7.
|
13
|
+
hydraflow-0.7.5.dist-info/METADATA,sha256=oSBWEevJs2RI55hqrxzW3k9ArtwRrvnk1kBl7oJNohg,4767
|
14
|
+
hydraflow-0.7.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
15
|
+
hydraflow-0.7.5.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
|
16
|
+
hydraflow-0.7.5.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
17
|
+
hydraflow-0.7.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|