hydraflow 0.2.16__py3-none-any.whl → 0.2.18__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/asyncio.py +13 -11
- hydraflow/config.py +3 -6
- hydraflow/context.py +15 -15
- hydraflow/info.py +16 -6
- hydraflow/mlflow.py +36 -23
- hydraflow/param.py +75 -0
- hydraflow/progress.py +7 -18
- hydraflow/run_collection.py +122 -99
- {hydraflow-0.2.16.dist-info → hydraflow-0.2.18.dist-info}/METADATA +1 -1
- hydraflow-0.2.18.dist-info/RECORD +14 -0
- hydraflow-0.2.16.dist-info/RECORD +0 -13
- {hydraflow-0.2.16.dist-info → hydraflow-0.2.18.dist-info}/WHEEL +0 -0
- {hydraflow-0.2.16.dist-info → hydraflow-0.2.18.dist-info}/licenses/LICENSE +0 -0
hydraflow/asyncio.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Provide functionality for running commands and monitoring file changes."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
import asyncio
|
@@ -27,8 +29,7 @@ async def execute_command(
|
|
27
29
|
stderr: Callable[[str], None] | None = None,
|
28
30
|
stop_event: asyncio.Event,
|
29
31
|
) -> int:
|
30
|
-
"""
|
31
|
-
Runs a command asynchronously and pass the output to callback functions.
|
32
|
+
"""Run a command asynchronously and pass the output to callback functions.
|
32
33
|
|
33
34
|
Args:
|
34
35
|
program (str): The program to run.
|
@@ -39,6 +40,7 @@ async def execute_command(
|
|
39
40
|
|
40
41
|
Returns:
|
41
42
|
int: The return code of the process.
|
43
|
+
|
42
44
|
"""
|
43
45
|
try:
|
44
46
|
process = await asyncio.create_subprocess_exec(
|
@@ -68,13 +70,13 @@ async def process_stream(
|
|
68
70
|
stream: StreamReader | None,
|
69
71
|
callback: Callable[[str], None] | None,
|
70
72
|
) -> None:
|
71
|
-
"""
|
72
|
-
Reads a stream asynchronously and pass each line to a callback function.
|
73
|
+
"""Read a stream asynchronously and pass each line to a callback function.
|
73
74
|
|
74
75
|
Args:
|
75
76
|
stream (StreamReader | None): The stream to read from.
|
76
77
|
callback (Callable[[str], None] | None): The callback function to handle
|
77
78
|
each line.
|
79
|
+
|
78
80
|
"""
|
79
81
|
if stream is None or callback is None:
|
80
82
|
return
|
@@ -93,9 +95,7 @@ async def monitor_file_changes(
|
|
93
95
|
stop_event: asyncio.Event,
|
94
96
|
**awatch_kwargs,
|
95
97
|
) -> None:
|
96
|
-
"""
|
97
|
-
Watches for file changes in specified paths and pass the changes to a
|
98
|
-
callback function.
|
98
|
+
"""Watch file changes in specified paths and pass the changes to a callback.
|
99
99
|
|
100
100
|
Args:
|
101
101
|
paths (list[str | Path]): List of paths to monitor for changes.
|
@@ -103,6 +103,7 @@ async def monitor_file_changes(
|
|
103
103
|
function to handle file changes.
|
104
104
|
stop_event (asyncio.Event): Event to signal when to stop watching.
|
105
105
|
**awatch_kwargs: Additional keyword arguments to pass to watchfiles.awatch.
|
106
|
+
|
106
107
|
"""
|
107
108
|
str_paths = [str(path) for path in paths]
|
108
109
|
try:
|
@@ -127,8 +128,7 @@ async def run_and_monitor(
|
|
127
128
|
paths: list[str | Path] | None = None,
|
128
129
|
**awatch_kwargs,
|
129
130
|
) -> int:
|
130
|
-
"""
|
131
|
-
Runs a command and optionally watch for file changes concurrently.
|
131
|
+
"""Run a command and optionally watch for file changes concurrently.
|
132
132
|
|
133
133
|
Args:
|
134
134
|
program (str): The program to run.
|
@@ -138,6 +138,8 @@ async def run_and_monitor(
|
|
138
138
|
watch (Callable[[set[tuple[Change, str]]], None] | None): Callback for
|
139
139
|
file changes.
|
140
140
|
paths (list[str | Path] | None): List of paths to monitor for changes.
|
141
|
+
**awatch_kwargs: Additional keyword arguments to pass to `watchfiles.awatch`.
|
142
|
+
|
141
143
|
"""
|
142
144
|
stop_event = asyncio.Event()
|
143
145
|
run_task = asyncio.create_task(
|
@@ -184,8 +186,7 @@ def run(
|
|
184
186
|
paths: list[str | Path] | None = None,
|
185
187
|
**awatch_kwargs,
|
186
188
|
) -> int:
|
187
|
-
"""
|
188
|
-
Run a command synchronously and optionally watch for file changes.
|
189
|
+
"""Run a command synchronously and optionally watch for file changes.
|
189
190
|
|
190
191
|
This function is a synchronous wrapper around the asynchronous
|
191
192
|
`run_and_monitor` function. It runs a specified command and optionally
|
@@ -208,6 +209,7 @@ def run(
|
|
208
209
|
|
209
210
|
Returns:
|
210
211
|
int: The return code of the process.
|
212
|
+
|
211
213
|
"""
|
212
214
|
if watch and not paths:
|
213
215
|
paths = [Path.cwd()]
|
hydraflow/config.py
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
This module provides functionality for working with configuration
|
3
|
-
objects using the OmegaConf library.
|
4
|
-
"""
|
1
|
+
"""Provide functionality for working with configuration objects using the OmegaConf."""
|
5
2
|
|
6
3
|
from __future__ import annotations
|
7
4
|
|
@@ -15,8 +12,7 @@ if TYPE_CHECKING:
|
|
15
12
|
|
16
13
|
|
17
14
|
def iter_params(config: object, prefix: str = "") -> Iterator[tuple[str, Any]]:
|
18
|
-
"""
|
19
|
-
Recursively iterate over the parameters in the given configuration object.
|
15
|
+
"""Recursively iterate over the parameters in the given configuration object.
|
20
16
|
|
21
17
|
This function traverses the configuration object and yields key-value pairs
|
22
18
|
representing the parameters. The keys are prefixed with the provided prefix.
|
@@ -29,6 +25,7 @@ def iter_params(config: object, prefix: str = "") -> Iterator[tuple[str, Any]]:
|
|
29
25
|
|
30
26
|
Yields:
|
31
27
|
Key-value pairs representing the parameters in the configuration object.
|
28
|
+
|
32
29
|
"""
|
33
30
|
if config is None:
|
34
31
|
return
|
hydraflow/context.py
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
This module provides context managers to log parameters and manage the MLflow
|
3
|
-
run context.
|
4
|
-
"""
|
1
|
+
"""Provide context managers to log parameters and manage the MLflow run context."""
|
5
2
|
|
6
3
|
from __future__ import annotations
|
7
4
|
|
@@ -34,9 +31,7 @@ def log_run(
|
|
34
31
|
*,
|
35
32
|
synchronous: bool | None = None,
|
36
33
|
) -> Iterator[None]:
|
37
|
-
"""
|
38
|
-
Log the parameters from the given configuration object and manage the MLflow
|
39
|
-
run context.
|
34
|
+
"""Log the parameters from the given configuration object.
|
40
35
|
|
41
36
|
This context manager logs the parameters from the provided configuration object
|
42
37
|
using MLflow. It also manages the MLflow run context, ensuring that artifacts
|
@@ -56,6 +51,7 @@ def log_run(
|
|
56
51
|
# Perform operations within the MLflow run context
|
57
52
|
pass
|
58
53
|
```
|
54
|
+
|
59
55
|
"""
|
60
56
|
log_params(config, synchronous=synchronous)
|
61
57
|
|
@@ -98,8 +94,7 @@ def start_run( # noqa: PLR0913
|
|
98
94
|
log_system_metrics: bool | None = None,
|
99
95
|
synchronous: bool | None = None,
|
100
96
|
) -> Iterator[Run]:
|
101
|
-
"""
|
102
|
-
Start an MLflow run and log parameters using the provided configuration object.
|
97
|
+
"""Start an MLflow run and log parameters using the provided configuration object.
|
103
98
|
|
104
99
|
This context manager starts an MLflow run and logs parameters using the specified
|
105
100
|
configuration object. It ensures that the run is properly closed after completion.
|
@@ -130,6 +125,7 @@ def start_run( # noqa: PLR0913
|
|
130
125
|
- `mlflow.start_run`: The MLflow function to start a run directly.
|
131
126
|
- `log_run`: A context manager to log parameters and manage the MLflow
|
132
127
|
run context.
|
128
|
+
|
133
129
|
"""
|
134
130
|
with (
|
135
131
|
mlflow.start_run(
|
@@ -156,9 +152,7 @@ def watch(
|
|
156
152
|
ignore_patterns: list[str] | None = None,
|
157
153
|
ignore_log: bool = True,
|
158
154
|
) -> Iterator[None]:
|
159
|
-
"""
|
160
|
-
Watch the given directory for changes and call the provided function
|
161
|
-
when a change is detected.
|
155
|
+
"""Watch the given directory for changes.
|
162
156
|
|
163
157
|
This context manager sets up a file system watcher on the specified directory.
|
164
158
|
When a file modification is detected, the provided function is called with
|
@@ -173,6 +167,9 @@ def watch(
|
|
173
167
|
the current MLflow artifact URI is used. Defaults to "".
|
174
168
|
timeout (int): The timeout period in seconds for the watcher
|
175
169
|
to run after the context is exited. Defaults to 60.
|
170
|
+
ignore_patterns (list[str] | None): A list of glob patterns to ignore.
|
171
|
+
Defaults to None.
|
172
|
+
ignore_log (bool): Whether to ignore log files. Defaults to True.
|
176
173
|
|
177
174
|
Yields:
|
178
175
|
None
|
@@ -183,6 +180,7 @@ def watch(
|
|
183
180
|
# Perform operations while watching the directory for changes
|
184
181
|
pass
|
185
182
|
```
|
183
|
+
|
186
184
|
"""
|
187
185
|
dir = dir or get_artifact_dir() # noqa: A001
|
188
186
|
if isinstance(dir, Path):
|
@@ -214,6 +212,8 @@ def watch(
|
|
214
212
|
|
215
213
|
|
216
214
|
class Handler(PatternMatchingEventHandler):
|
215
|
+
"""Monitor file changes and call the given function when a change is detected."""
|
216
|
+
|
217
217
|
def __init__(
|
218
218
|
self,
|
219
219
|
func: Callable[[Path], None],
|
@@ -232,6 +232,7 @@ class Handler(PatternMatchingEventHandler):
|
|
232
232
|
super().__init__(ignore_patterns=ignore_patterns)
|
233
233
|
|
234
234
|
def on_modified(self, event: FileModifiedEvent) -> None:
|
235
|
+
"""Modify when a file is modified."""
|
235
236
|
file = Path(str(event.src_path))
|
236
237
|
if file.is_file():
|
237
238
|
self.func(file)
|
@@ -242,9 +243,7 @@ def chdir_artifact(
|
|
242
243
|
run: Run,
|
243
244
|
artifact_path: str | None = None,
|
244
245
|
) -> Iterator[Path]:
|
245
|
-
"""
|
246
|
-
Change the current working directory to the artifact directory of the
|
247
|
-
given run.
|
246
|
+
"""Change the current working directory to the artifact directory of the given run.
|
248
247
|
|
249
248
|
This context manager changes the current working directory to the artifact
|
250
249
|
directory of the given run. It ensures that the directory is changed back
|
@@ -253,6 +252,7 @@ def chdir_artifact(
|
|
253
252
|
Args:
|
254
253
|
run (Run): The run to get the artifact directory from.
|
255
254
|
artifact_path (str | None): The artifact path.
|
255
|
+
|
256
256
|
"""
|
257
257
|
curdir = Path.cwd()
|
258
258
|
path = mlflow.artifacts.download_artifacts(
|
hydraflow/info.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Provide information about MLflow runs."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
from pathlib import Path
|
@@ -15,37 +17,44 @@ if TYPE_CHECKING:
|
|
15
17
|
|
16
18
|
|
17
19
|
class RunCollectionInfo:
|
20
|
+
"""Provide information about MLflow runs."""
|
21
|
+
|
18
22
|
def __init__(self, runs: RunCollection) -> None:
|
19
23
|
self._runs = runs
|
20
24
|
|
21
25
|
@property
|
22
26
|
def run_id(self) -> list[str]:
|
27
|
+
"""Get the run ID for each run in the collection."""
|
23
28
|
return [run.info.run_id for run in self._runs]
|
24
29
|
|
25
30
|
@property
|
26
31
|
def params(self) -> list[dict[str, str]]:
|
32
|
+
"""Get the parameters for each run in the collection."""
|
27
33
|
return [run.data.params for run in self._runs]
|
28
34
|
|
29
35
|
@property
|
30
36
|
def metrics(self) -> list[dict[str, float]]:
|
37
|
+
"""Get the metrics for each run in the collection."""
|
31
38
|
return [run.data.metrics for run in self._runs]
|
32
39
|
|
33
40
|
@property
|
34
41
|
def artifact_uri(self) -> list[str | None]:
|
42
|
+
"""Get the artifact URI for each run in the collection."""
|
35
43
|
return [run.info.artifact_uri for run in self._runs]
|
36
44
|
|
37
45
|
@property
|
38
46
|
def artifact_dir(self) -> list[Path]:
|
47
|
+
"""Get the artifact directory for each run in the collection."""
|
39
48
|
return [get_artifact_dir(run) for run in self._runs]
|
40
49
|
|
41
50
|
@property
|
42
51
|
def config(self) -> list[DictConfig]:
|
52
|
+
"""Get the configuration for each run in the collection."""
|
43
53
|
return [load_config(run) for run in self._runs]
|
44
54
|
|
45
55
|
|
46
56
|
def get_artifact_dir(run: Run | None = None) -> Path:
|
47
|
-
"""
|
48
|
-
Retrieve the artifact directory for the given run.
|
57
|
+
"""Retrieve the artifact directory for the given run.
|
49
58
|
|
50
59
|
This function uses MLflow to get the artifact directory for the given run.
|
51
60
|
|
@@ -54,6 +63,7 @@ def get_artifact_dir(run: Run | None = None) -> Path:
|
|
54
63
|
|
55
64
|
Returns:
|
56
65
|
The local path to the directory where the artifacts are downloaded.
|
66
|
+
|
57
67
|
"""
|
58
68
|
if run is None:
|
59
69
|
uri = mlflow.get_artifact_uri()
|
@@ -64,8 +74,7 @@ def get_artifact_dir(run: Run | None = None) -> Path:
|
|
64
74
|
|
65
75
|
|
66
76
|
def get_hydra_output_dir(run: Run | None = None) -> Path:
|
67
|
-
"""
|
68
|
-
Retrieve the Hydra output directory for the given run.
|
77
|
+
"""Retrieve the Hydra output directory for the given run.
|
69
78
|
|
70
79
|
This function returns the Hydra output directory. If no run is provided,
|
71
80
|
it retrieves the output directory from the current Hydra configuration.
|
@@ -82,6 +91,7 @@ def get_hydra_output_dir(run: Run | None = None) -> Path:
|
|
82
91
|
Raises:
|
83
92
|
FileNotFoundError: If the Hydra configuration file is not found
|
84
93
|
in the artifacts.
|
94
|
+
|
85
95
|
"""
|
86
96
|
if run is None:
|
87
97
|
hc = HydraConfig.get()
|
@@ -97,8 +107,7 @@ def get_hydra_output_dir(run: Run | None = None) -> Path:
|
|
97
107
|
|
98
108
|
|
99
109
|
def load_config(run: Run) -> DictConfig:
|
100
|
-
"""
|
101
|
-
Load the configuration for a given run.
|
110
|
+
"""Load the configuration for a given run.
|
102
111
|
|
103
112
|
This function loads the configuration for the provided Run instance
|
104
113
|
by downloading the configuration file from the MLflow artifacts and
|
@@ -111,6 +120,7 @@ def load_config(run: Run) -> DictConfig:
|
|
111
120
|
Returns:
|
112
121
|
The loaded configuration as a DictConfig object. Returns an empty
|
113
122
|
DictConfig if the configuration file is not found.
|
123
|
+
|
114
124
|
"""
|
115
125
|
path = get_artifact_dir(run) / ".hydra/config.yaml"
|
116
126
|
return OmegaConf.load(path) # type: ignore
|
hydraflow/mlflow.py
CHANGED
@@ -1,20 +1,17 @@
|
|
1
|
-
"""
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
"""Provide functionality to log parameters from Hydra configuration objects.
|
2
|
+
|
3
|
+
This module provides functions to log parameters from Hydra configuration objects
|
4
|
+
to MLflow, set experiments, and manage tracking URIs. It integrates Hydra's
|
5
|
+
configuration management with MLflow's experiment tracking capabilities.
|
5
6
|
|
6
7
|
Key Features:
|
7
|
-
- **Experiment Management**: Set
|
8
|
-
|
9
|
-
- **
|
10
|
-
|
11
|
-
- **Run
|
12
|
-
|
13
|
-
|
14
|
-
easy access to outputs generated during experiments.
|
15
|
-
|
16
|
-
This module is designed to integrate seamlessly with Hydra, providing a robust
|
17
|
-
solution for tracking machine learning experiments and their associated metadata.
|
8
|
+
- **Experiment Management**: Set experiment names and tracking URIs using Hydra
|
9
|
+
configuration details.
|
10
|
+
- **Parameter Logging**: Log parameters from Hydra configuration objects to MLflow,
|
11
|
+
supporting both synchronous and asynchronous logging.
|
12
|
+
- **Run Collection**: Utilize the `RunCollection` class to manage and interact with
|
13
|
+
multiple MLflow runs, providing methods to filter and retrieve runs based on
|
14
|
+
various criteria.
|
18
15
|
"""
|
19
16
|
|
20
17
|
from __future__ import annotations
|
@@ -40,8 +37,7 @@ def set_experiment(
|
|
40
37
|
suffix: str = "",
|
41
38
|
uri: str | Path | None = None,
|
42
39
|
) -> Experiment:
|
43
|
-
"""
|
44
|
-
Sets the experiment name and tracking URI optionally.
|
40
|
+
"""Set the experiment name and tracking URI optionally.
|
45
41
|
|
46
42
|
This function sets the experiment name by combining the given prefix,
|
47
43
|
the job name from HydraConfig, and the given suffix. Optionally, it can
|
@@ -55,6 +51,7 @@ def set_experiment(
|
|
55
51
|
Returns:
|
56
52
|
Experiment: An instance of `mlflow.entities.Experiment` representing
|
57
53
|
the new active experiment.
|
54
|
+
|
58
55
|
"""
|
59
56
|
if uri is not None:
|
60
57
|
mlflow.set_tracking_uri(uri)
|
@@ -65,8 +62,7 @@ def set_experiment(
|
|
65
62
|
|
66
63
|
|
67
64
|
def log_params(config: object, *, synchronous: bool | None = None) -> None:
|
68
|
-
"""
|
69
|
-
Log the parameters from the given configuration object.
|
65
|
+
"""Log the parameters from the given configuration object.
|
70
66
|
|
71
67
|
This method logs the parameters from the provided configuration object
|
72
68
|
using MLflow. It iterates over the parameters and logs them using the
|
@@ -76,6 +72,7 @@ def log_params(config: object, *, synchronous: bool | None = None) -> None:
|
|
76
72
|
config (object): The configuration object to log the parameters from.
|
77
73
|
synchronous (bool | None): Whether to log the parameters synchronously.
|
78
74
|
Defaults to None.
|
75
|
+
|
79
76
|
"""
|
80
77
|
for key, value in iter_params(config):
|
81
78
|
mlflow.log_param(key, value, synchronous=synchronous)
|
@@ -91,8 +88,7 @@ def search_runs( # noqa: PLR0913
|
|
91
88
|
search_all_experiments: bool = False,
|
92
89
|
experiment_names: list[str] | None = None,
|
93
90
|
) -> RunCollection:
|
94
|
-
"""
|
95
|
-
Search for Runs that fit the specified criteria.
|
91
|
+
"""Search for Runs that fit the specified criteria.
|
96
92
|
|
97
93
|
This function wraps the `mlflow.search_runs` function and returns the
|
98
94
|
results as a `RunCollection` object. It allows for flexible searching of
|
@@ -133,6 +129,7 @@ def search_runs( # noqa: PLR0913
|
|
133
129
|
|
134
130
|
Returns:
|
135
131
|
A `RunCollection` object containing the search results.
|
132
|
+
|
136
133
|
"""
|
137
134
|
runs = mlflow.search_runs(
|
138
135
|
experiment_ids=experiment_ids,
|
@@ -151,9 +148,9 @@ def search_runs( # noqa: PLR0913
|
|
151
148
|
def list_runs(
|
152
149
|
experiment_names: str | list[str] | None = None,
|
153
150
|
n_jobs: int = 0,
|
151
|
+
status: str | list[str] | int | list[int] | None = None,
|
154
152
|
) -> RunCollection:
|
155
|
-
"""
|
156
|
-
List all runs for the specified experiments.
|
153
|
+
"""List all runs for the specified experiments.
|
157
154
|
|
158
155
|
This function retrieves all runs for the given list of experiment names.
|
159
156
|
If no experiment names are provided (None), it defaults to searching all runs
|
@@ -169,11 +166,27 @@ def list_runs(
|
|
169
166
|
for runs. If None or an empty list is provided, the function will
|
170
167
|
search the currently active experiment or all experiments except
|
171
168
|
the "Default" experiment.
|
169
|
+
n_jobs (int): The number of jobs to run in parallel. If 0, the function
|
170
|
+
will search runs sequentially.
|
171
|
+
status (str | list[str] | int | list[int] | None): The status of the runs
|
172
|
+
to filter.
|
172
173
|
|
173
174
|
Returns:
|
174
175
|
RunCollection: A `RunCollection` instance containing the runs for the
|
175
176
|
specified experiments.
|
177
|
+
|
176
178
|
"""
|
179
|
+
rc = _list_runs(experiment_names, n_jobs)
|
180
|
+
if status is None:
|
181
|
+
return rc
|
182
|
+
|
183
|
+
return rc.filter(status=status)
|
184
|
+
|
185
|
+
|
186
|
+
def _list_runs(
|
187
|
+
experiment_names: str | list[str] | None = None,
|
188
|
+
n_jobs: int = 0,
|
189
|
+
) -> RunCollection:
|
177
190
|
if isinstance(experiment_names, str):
|
178
191
|
experiment_names = [experiment_names]
|
179
192
|
|
hydraflow/param.py
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
"""Provide utility functions for parameter matching.
|
2
|
+
|
3
|
+
The main function `match` checks if a given parameter matches a specified value.
|
4
|
+
It supports various types of values including None, boolean, list, tuple, int,
|
5
|
+
float, and str.
|
6
|
+
|
7
|
+
Helper functions `_match_list` and `_match_tuple` are used internally to handle
|
8
|
+
matching for list and tuple types respectively.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from __future__ import annotations
|
12
|
+
|
13
|
+
from typing import Any
|
14
|
+
|
15
|
+
|
16
|
+
def match(param: str, value: Any) -> bool:
|
17
|
+
"""Check if the string matches the specified value.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
param (str): The parameter to check.
|
21
|
+
value (Any): The value to check.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
True if the parameter matches the specified value,
|
25
|
+
False otherwise.
|
26
|
+
|
27
|
+
"""
|
28
|
+
if value in [None, True, False]:
|
29
|
+
return param == str(value)
|
30
|
+
|
31
|
+
if isinstance(value, list) and (m := _match_list(param, value)) is not None:
|
32
|
+
return m
|
33
|
+
|
34
|
+
if isinstance(value, tuple) and (m := _match_tuple(param, value)) is not None:
|
35
|
+
return m
|
36
|
+
|
37
|
+
if isinstance(value, int | float | str):
|
38
|
+
return type(value)(param) == value
|
39
|
+
|
40
|
+
return param == str(value)
|
41
|
+
|
42
|
+
|
43
|
+
def _match_list(param: str, value: list) -> bool | None:
|
44
|
+
if not value:
|
45
|
+
return None
|
46
|
+
|
47
|
+
if any(param.startswith(x) for x in ["[", "(", "{"]):
|
48
|
+
return None
|
49
|
+
|
50
|
+
if isinstance(value[0], bool):
|
51
|
+
return None
|
52
|
+
|
53
|
+
if not isinstance(value[0], int | float | str):
|
54
|
+
return None
|
55
|
+
|
56
|
+
return type(value[0])(param) in value
|
57
|
+
|
58
|
+
|
59
|
+
def _match_tuple(param: str, value: tuple) -> bool | None:
|
60
|
+
if len(value) != 2: # noqa: PLR2004
|
61
|
+
return None
|
62
|
+
|
63
|
+
if any(param.startswith(x) for x in ["[", "(", "{"]):
|
64
|
+
return None
|
65
|
+
|
66
|
+
if isinstance(value[0], bool):
|
67
|
+
return None
|
68
|
+
|
69
|
+
if not isinstance(value[0], int | float | str):
|
70
|
+
return None
|
71
|
+
|
72
|
+
if type(value[0]) is not type(value[1]):
|
73
|
+
return None
|
74
|
+
|
75
|
+
return value[0] <= type(value[0])(param) < value[1] # type: ignore
|
hydraflow/progress.py
CHANGED
@@ -1,18 +1,7 @@
|
|
1
|
-
"""
|
2
|
-
Module for managing progress tracking in parallel processing using Joblib
|
3
|
-
and Rich's Progress bar.
|
1
|
+
"""Context managers and functions for parallel task execution with progress.
|
4
2
|
|
5
3
|
Provide context managers and functions to facilitate the execution
|
6
4
|
of tasks in parallel while displaying progress updates.
|
7
|
-
|
8
|
-
The following key components are provided:
|
9
|
-
|
10
|
-
- JoblibProgress: A context manager for tracking progress with Rich's progress
|
11
|
-
bar.
|
12
|
-
- parallel_progress: A function to execute a given function in parallel over
|
13
|
-
an iterable with progress tracking.
|
14
|
-
- multi_tasks_progress: A function to render auto-updating progress bars for
|
15
|
-
multiple tasks concurrently.
|
16
5
|
"""
|
17
6
|
|
18
7
|
from __future__ import annotations
|
@@ -37,8 +26,7 @@ def JoblibProgress( # noqa: N802
|
|
37
26
|
total: int | None = None,
|
38
27
|
**kwargs,
|
39
28
|
) -> Iterator[Progress]:
|
40
|
-
"""
|
41
|
-
Context manager for tracking progress using Joblib with Rich's Progress bar.
|
29
|
+
"""Context manager for tracking progress using Joblib with Rich's Progress bar.
|
42
30
|
|
43
31
|
Args:
|
44
32
|
*columns (ProgressColumn | str): Columns to display in the progress bar.
|
@@ -56,6 +44,7 @@ def JoblibProgress( # noqa: N802
|
|
56
44
|
with JoblibProgress("task", total=100) as progress:
|
57
45
|
# Your parallel processing code here
|
58
46
|
```
|
47
|
+
|
59
48
|
"""
|
60
49
|
if not columns:
|
61
50
|
columns = Progress.get_default_columns()
|
@@ -94,8 +83,7 @@ def parallel_progress(
|
|
94
83
|
description: str | None = None,
|
95
84
|
**kwargs,
|
96
85
|
) -> list[U]:
|
97
|
-
"""
|
98
|
-
Execute a function in parallel over an iterable with progress tracking.
|
86
|
+
"""Execute a function in parallel over an iterable with progress tracking.
|
99
87
|
|
100
88
|
Args:
|
101
89
|
func (Callable[[T], U]): The function to execute on each item in the
|
@@ -112,6 +100,7 @@ def parallel_progress(
|
|
112
100
|
Returns:
|
113
101
|
list[U]: A list of results from applying the function to each item in
|
114
102
|
the iterable.
|
103
|
+
|
115
104
|
"""
|
116
105
|
iterable = list(iterable)
|
117
106
|
total = len(iterable)
|
@@ -130,8 +119,7 @@ def multi_tasks_progress(
|
|
130
119
|
transient: bool | None = None,
|
131
120
|
**kwargs,
|
132
121
|
) -> None:
|
133
|
-
"""
|
134
|
-
Render auto-updating progress bars for multiple tasks concurrently.
|
122
|
+
"""Render auto-updating progress bars for multiple tasks concurrently.
|
135
123
|
|
136
124
|
Args:
|
137
125
|
iterables (Iterable[Iterable[int | tuple[int, int]]]): A collection of
|
@@ -151,6 +139,7 @@ def multi_tasks_progress(
|
|
151
139
|
|
152
140
|
Returns:
|
153
141
|
None
|
142
|
+
|
154
143
|
"""
|
155
144
|
if not columns:
|
156
145
|
columns = Progress.get_default_columns()
|
hydraflow/run_collection.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
"""
|
2
|
-
|
3
|
-
|
1
|
+
"""Provide a collection of MLflow runs.
|
2
|
+
|
3
|
+
This module includes the `RunCollection` class, which serves as a container
|
4
4
|
for multiple MLflow `Run` instances, and various methods to filter and
|
5
5
|
retrieve these runs.
|
6
6
|
|
@@ -23,6 +23,9 @@ from dataclasses import dataclass, field
|
|
23
23
|
from itertools import chain
|
24
24
|
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar, overload
|
25
25
|
|
26
|
+
from mlflow.entities import RunStatus
|
27
|
+
|
28
|
+
import hydraflow.param
|
26
29
|
from hydraflow.config import iter_params
|
27
30
|
from hydraflow.info import RunCollectionInfo
|
28
31
|
|
@@ -41,8 +44,7 @@ P = ParamSpec("P")
|
|
41
44
|
|
42
45
|
@dataclass
|
43
46
|
class RunCollection:
|
44
|
-
"""
|
45
|
-
Represent a collection of MLflow runs.
|
47
|
+
"""Represent a collection of MLflow runs.
|
46
48
|
|
47
49
|
Provide methods to interact with the runs, such as filtering,
|
48
50
|
retrieving specific runs, and accessing run information.
|
@@ -86,10 +88,12 @@ class RunCollection:
|
|
86
88
|
def __contains__(self, run: Run) -> bool:
|
87
89
|
return run in self._runs
|
88
90
|
|
91
|
+
def __bool__(self) -> bool:
|
92
|
+
return bool(self._runs)
|
93
|
+
|
89
94
|
@classmethod
|
90
95
|
def from_list(cls, runs: list[Run]) -> RunCollection:
|
91
96
|
"""Create a `RunCollection` instance from a list of MLflow `Run` instances."""
|
92
|
-
|
93
97
|
return cls(runs)
|
94
98
|
|
95
99
|
@property
|
@@ -110,6 +114,7 @@ class RunCollection:
|
|
110
114
|
Returns:
|
111
115
|
A new `RunCollection` instance containing the first n runs if n is
|
112
116
|
positive, or the last n runs if n is negative.
|
117
|
+
|
113
118
|
"""
|
114
119
|
if n < 0:
|
115
120
|
return self.__class__(self._runs[n:])
|
@@ -122,17 +127,28 @@ class RunCollection:
|
|
122
127
|
*,
|
123
128
|
reverse: bool = False,
|
124
129
|
) -> None:
|
130
|
+
"""Sort the runs in the collection.
|
131
|
+
|
132
|
+
Sort the runs in the collection according to the provided key function
|
133
|
+
and optional reverse flag.
|
134
|
+
|
135
|
+
Args:
|
136
|
+
key (Callable[[Run], Any] | None): A function that takes a run and returns
|
137
|
+
a value to sort by.
|
138
|
+
reverse (bool): If True, sort in descending order.
|
139
|
+
|
140
|
+
"""
|
125
141
|
self._runs.sort(key=key or (lambda x: x.info.start_time), reverse=reverse)
|
126
142
|
|
127
143
|
def one(self) -> Run:
|
128
|
-
"""
|
129
|
-
Get the only `Run` instance in the collection.
|
144
|
+
"""Get the only `Run` instance in the collection.
|
130
145
|
|
131
146
|
Returns:
|
132
147
|
The only `Run` instance in the collection.
|
133
148
|
|
134
149
|
Raises:
|
135
150
|
ValueError: If the collection does not contain exactly one run.
|
151
|
+
|
136
152
|
"""
|
137
153
|
if len(self._runs) != 1:
|
138
154
|
raise ValueError("The collection does not contain exactly one run.")
|
@@ -140,24 +156,24 @@ class RunCollection:
|
|
140
156
|
return self._runs[0]
|
141
157
|
|
142
158
|
def try_one(self) -> Run | None:
|
143
|
-
"""
|
144
|
-
Try to get the only `Run` instance in the collection.
|
159
|
+
"""Try to get the only `Run` instance in the collection.
|
145
160
|
|
146
161
|
Returns:
|
147
162
|
The only `Run` instance in the collection, or None if the collection
|
148
163
|
does not contain exactly one run.
|
164
|
+
|
149
165
|
"""
|
150
166
|
return self._runs[0] if len(self._runs) == 1 else None
|
151
167
|
|
152
168
|
def first(self) -> Run:
|
153
|
-
"""
|
154
|
-
Get the first `Run` instance in the collection.
|
169
|
+
"""Get the first `Run` instance in the collection.
|
155
170
|
|
156
171
|
Returns:
|
157
172
|
The first `Run` instance in the collection.
|
158
173
|
|
159
174
|
Raises:
|
160
175
|
ValueError: If the collection is empty.
|
176
|
+
|
161
177
|
"""
|
162
178
|
if not self._runs:
|
163
179
|
raise ValueError("The collection is empty.")
|
@@ -165,24 +181,24 @@ class RunCollection:
|
|
165
181
|
return self._runs[0]
|
166
182
|
|
167
183
|
def try_first(self) -> Run | None:
|
168
|
-
"""
|
169
|
-
Try to get the first `Run` instance in the collection.
|
184
|
+
"""Try to get the first `Run` instance in the collection.
|
170
185
|
|
171
186
|
Returns:
|
172
187
|
The first `Run` instance in the collection, or None if the collection
|
173
188
|
is empty.
|
189
|
+
|
174
190
|
"""
|
175
191
|
return self._runs[0] if self._runs else None
|
176
192
|
|
177
193
|
def last(self) -> Run:
|
178
|
-
"""
|
179
|
-
Get the last `Run` instance in the collection.
|
194
|
+
"""Get the last `Run` instance in the collection.
|
180
195
|
|
181
196
|
Returns:
|
182
197
|
The last `Run` instance in the collection.
|
183
198
|
|
184
199
|
Raises:
|
185
200
|
ValueError: If the collection is empty.
|
201
|
+
|
186
202
|
"""
|
187
203
|
if not self._runs:
|
188
204
|
raise ValueError("The collection is empty.")
|
@@ -190,18 +206,17 @@ class RunCollection:
|
|
190
206
|
return self._runs[-1]
|
191
207
|
|
192
208
|
def try_last(self) -> Run | None:
|
193
|
-
"""
|
194
|
-
Try to get the last `Run` instance in the collection.
|
209
|
+
"""Try to get the last `Run` instance in the collection.
|
195
210
|
|
196
211
|
Returns:
|
197
212
|
The last `Run` instance in the collection, or None if the collection
|
198
213
|
is empty.
|
214
|
+
|
199
215
|
"""
|
200
216
|
return self._runs[-1] if self._runs else None
|
201
217
|
|
202
218
|
def filter(self, config: object | None = None, **kwargs) -> RunCollection:
|
203
|
-
"""
|
204
|
-
Filter the `Run` instances based on the provided configuration.
|
219
|
+
"""Filter the `Run` instances based on the provided configuration.
|
205
220
|
|
206
221
|
This method filters the runs in the collection according to the
|
207
222
|
specified configuration object and additional key-value pairs. The
|
@@ -224,12 +239,12 @@ class RunCollection:
|
|
224
239
|
|
225
240
|
Returns:
|
226
241
|
A new `RunCollection` object containing the filtered runs.
|
242
|
+
|
227
243
|
"""
|
228
244
|
return RunCollection(filter_runs(self._runs, config, **kwargs))
|
229
245
|
|
230
246
|
def find(self, config: object | None = None, **kwargs) -> Run:
|
231
|
-
"""
|
232
|
-
Find the first `Run` instance based on the provided configuration.
|
247
|
+
"""Find the first `Run` instance based on the provided configuration.
|
233
248
|
|
234
249
|
This method filters the runs in the collection according to the
|
235
250
|
specified configuration object and returns the first run that matches
|
@@ -248,6 +263,7 @@ class RunCollection:
|
|
248
263
|
|
249
264
|
See Also:
|
250
265
|
`filter`: Perform the actual filtering logic.
|
266
|
+
|
251
267
|
"""
|
252
268
|
try:
|
253
269
|
return self.filter(config, **kwargs).first()
|
@@ -255,8 +271,7 @@ class RunCollection:
|
|
255
271
|
raise ValueError("No run matches the provided configuration.")
|
256
272
|
|
257
273
|
def try_find(self, config: object | None = None, **kwargs) -> Run | None:
|
258
|
-
"""
|
259
|
-
Try to find the first `Run` instance based on the provided configuration.
|
274
|
+
"""Try to find the first `Run` instance based on the provided configuration.
|
260
275
|
|
261
276
|
This method filters the runs in the collection according to the
|
262
277
|
specified configuration object and returns the first run that matches
|
@@ -273,12 +288,12 @@ class RunCollection:
|
|
273
288
|
|
274
289
|
See Also:
|
275
290
|
`filter`: Perform the actual filtering logic.
|
291
|
+
|
276
292
|
"""
|
277
293
|
return self.filter(config, **kwargs).try_first()
|
278
294
|
|
279
295
|
def find_last(self, config: object | None = None, **kwargs) -> Run:
|
280
|
-
"""
|
281
|
-
Find the last `Run` instance based on the provided configuration.
|
296
|
+
"""Find the last `Run` instance based on the provided configuration.
|
282
297
|
|
283
298
|
This method filters the runs in the collection according to the
|
284
299
|
specified configuration object and returns the last run that matches
|
@@ -297,6 +312,7 @@ class RunCollection:
|
|
297
312
|
|
298
313
|
See Also:
|
299
314
|
`filter`: Perform the actual filtering logic.
|
315
|
+
|
300
316
|
"""
|
301
317
|
try:
|
302
318
|
return self.filter(config, **kwargs).last()
|
@@ -304,8 +320,7 @@ class RunCollection:
|
|
304
320
|
raise ValueError("No run matches the provided configuration.")
|
305
321
|
|
306
322
|
def try_find_last(self, config: object | None = None, **kwargs) -> Run | None:
|
307
|
-
"""
|
308
|
-
Try to find the last `Run` instance based on the provided configuration.
|
323
|
+
"""Try to find the last `Run` instance based on the provided configuration.
|
309
324
|
|
310
325
|
This method filters the runs in the collection according to the
|
311
326
|
specified configuration object and returns the last run that matches
|
@@ -322,12 +337,12 @@ class RunCollection:
|
|
322
337
|
|
323
338
|
See Also:
|
324
339
|
`filter`: Perform the actual filtering logic.
|
340
|
+
|
325
341
|
"""
|
326
342
|
return self.filter(config, **kwargs).try_last()
|
327
343
|
|
328
344
|
def get(self, config: object | None = None, **kwargs) -> Run:
|
329
|
-
"""
|
330
|
-
Retrieve a specific `Run` instance based on the provided configuration.
|
345
|
+
"""Retrieve a specific `Run` instance based on the provided configuration.
|
331
346
|
|
332
347
|
This method filters the runs in the collection according to the
|
333
348
|
specified configuration object and returns the run that matches the
|
@@ -347,6 +362,7 @@ class RunCollection:
|
|
347
362
|
|
348
363
|
See Also:
|
349
364
|
`filter`: Perform the actual filtering logic.
|
365
|
+
|
350
366
|
"""
|
351
367
|
try:
|
352
368
|
return self.filter(config, **kwargs).one()
|
@@ -355,8 +371,7 @@ class RunCollection:
|
|
355
371
|
raise ValueError(msg)
|
356
372
|
|
357
373
|
def try_get(self, config: object | None = None, **kwargs) -> Run | None:
|
358
|
-
"""
|
359
|
-
Try to retrieve a specific `Run` instance based on the provided configuration.
|
374
|
+
"""Try to retrieve a specific `Run` instance based on the provided config.
|
360
375
|
|
361
376
|
This method filters the runs in the collection according to the
|
362
377
|
specified configuration object and returns the run that matches the
|
@@ -376,12 +391,12 @@ class RunCollection:
|
|
376
391
|
|
377
392
|
See Also:
|
378
393
|
`filter`: Perform the actual filtering logic.
|
394
|
+
|
379
395
|
"""
|
380
396
|
return self.filter(config, **kwargs).try_one()
|
381
397
|
|
382
398
|
def get_param_names(self) -> list[str]:
|
383
|
-
"""
|
384
|
-
Get the parameter names from the runs.
|
399
|
+
"""Get the parameter names from the runs.
|
385
400
|
|
386
401
|
This method extracts the unique parameter names from the provided list
|
387
402
|
of runs. It iterates through each run and collects the parameter names
|
@@ -389,6 +404,7 @@ class RunCollection:
|
|
389
404
|
|
390
405
|
Returns:
|
391
406
|
A list of unique parameter names.
|
407
|
+
|
392
408
|
"""
|
393
409
|
param_names = set()
|
394
410
|
|
@@ -398,24 +414,30 @@ class RunCollection:
|
|
398
414
|
|
399
415
|
return list(param_names)
|
400
416
|
|
401
|
-
def get_param_dict(self) -> dict[str, list[str]]:
|
402
|
-
"""
|
403
|
-
Get the parameter dictionary from the list of runs.
|
417
|
+
def get_param_dict(self, *, drop_const: bool = False) -> dict[str, list[str]]:
|
418
|
+
"""Get the parameter dictionary from the list of runs.
|
404
419
|
|
405
420
|
This method extracts the parameter names and their corresponding values
|
406
421
|
from the provided list of runs. It iterates through each run and
|
407
422
|
collects the parameter values into a dictionary where the keys are
|
408
423
|
parameter names and the values are lists of parameter values.
|
409
424
|
|
425
|
+
Args:
|
426
|
+
drop_const (bool): If True, drop the parameter values that are constant
|
427
|
+
across all runs.
|
428
|
+
|
410
429
|
Returns:
|
411
430
|
A dictionary where the keys are parameter names and the values are
|
412
431
|
lists of parameter values.
|
432
|
+
|
413
433
|
"""
|
414
434
|
params = {}
|
415
435
|
|
416
436
|
for name in self.get_param_names():
|
417
437
|
it = (run.data.params[name] for run in self if name in run.data.params)
|
418
|
-
|
438
|
+
unique_values = sorted(set(it))
|
439
|
+
if not drop_const or len(unique_values) > 1:
|
440
|
+
params[name] = unique_values
|
419
441
|
|
420
442
|
return params
|
421
443
|
|
@@ -425,9 +447,7 @@ class RunCollection:
|
|
425
447
|
*args: P.args,
|
426
448
|
**kwargs: P.kwargs,
|
427
449
|
) -> Iterator[T]:
|
428
|
-
"""
|
429
|
-
Apply a function to each run in the collection and return an iterator of
|
430
|
-
results.
|
450
|
+
"""Return an iterator of results by applying a function to each run.
|
431
451
|
|
432
452
|
This method iterates over each run in the collection and applies the
|
433
453
|
provided function to it, along with any additional arguments and
|
@@ -441,6 +461,7 @@ class RunCollection:
|
|
441
461
|
|
442
462
|
Yields:
|
443
463
|
Results obtained by applying the function to each run in the collection.
|
464
|
+
|
444
465
|
"""
|
445
466
|
return (func(run, *args, **kwargs) for run in self)
|
446
467
|
|
@@ -450,9 +471,7 @@ class RunCollection:
|
|
450
471
|
*args: P.args,
|
451
472
|
**kwargs: P.kwargs,
|
452
473
|
) -> Iterator[T]:
|
453
|
-
"""
|
454
|
-
Apply a function to each run id in the collection and return an iterator
|
455
|
-
of results.
|
474
|
+
"""Return an iterator of results by applying a function to each run id.
|
456
475
|
|
457
476
|
Args:
|
458
477
|
func (Callable[[str, P], T]): A function that takes a run id and returns a
|
@@ -463,6 +482,7 @@ class RunCollection:
|
|
463
482
|
Yields:
|
464
483
|
Results obtained by applying the function to each run id in the
|
465
484
|
collection.
|
485
|
+
|
466
486
|
"""
|
467
487
|
return (func(run_id, *args, **kwargs) for run_id in self.info.run_id)
|
468
488
|
|
@@ -472,9 +492,7 @@ class RunCollection:
|
|
472
492
|
*args: P.args,
|
473
493
|
**kwargs: P.kwargs,
|
474
494
|
) -> Iterator[T]:
|
475
|
-
"""
|
476
|
-
Apply a function to each run configuration in the collection and return
|
477
|
-
an iterator of results.
|
495
|
+
"""Return an iterator of results by applying a function to each run config.
|
478
496
|
|
479
497
|
Args:
|
480
498
|
func (Callable[[DictConfig, P], T]): A function that takes a run
|
@@ -485,6 +503,7 @@ class RunCollection:
|
|
485
503
|
Yields:
|
486
504
|
Results obtained by applying the function to each run configuration
|
487
505
|
in the collection.
|
506
|
+
|
488
507
|
"""
|
489
508
|
return (func(config, *args, **kwargs) for config in self.info.config)
|
490
509
|
|
@@ -494,9 +513,7 @@ class RunCollection:
|
|
494
513
|
*args: P.args,
|
495
514
|
**kwargs: P.kwargs,
|
496
515
|
) -> Iterator[T]:
|
497
|
-
"""
|
498
|
-
Apply a function to each artifact URI in the collection and return an
|
499
|
-
iterator of results.
|
516
|
+
"""Return an iterator of results by applying a function to each artifact URI.
|
500
517
|
|
501
518
|
Iterate over each run in the collection, retrieves the artifact URI, and
|
502
519
|
apply the provided function to it. If a run does not have an artifact
|
@@ -511,6 +528,7 @@ class RunCollection:
|
|
511
528
|
Yields:
|
512
529
|
Results obtained by applying the function to each artifact URI in the
|
513
530
|
collection.
|
531
|
+
|
514
532
|
"""
|
515
533
|
return (func(uri, *args, **kwargs) for uri in self.info.artifact_uri)
|
516
534
|
|
@@ -520,9 +538,7 @@ class RunCollection:
|
|
520
538
|
*args: P.args,
|
521
539
|
**kwargs: P.kwargs,
|
522
540
|
) -> Iterator[T]:
|
523
|
-
"""
|
524
|
-
Apply a function to each artifact directory in the collection and return
|
525
|
-
an iterator of results.
|
541
|
+
"""Return an iterator of results by applying a function to each artifact dir.
|
526
542
|
|
527
543
|
Iterate over each run in the collection, downloads the artifact
|
528
544
|
directory, and apply the provided function to the directory path.
|
@@ -536,6 +552,7 @@ class RunCollection:
|
|
536
552
|
Yields:
|
537
553
|
Results obtained by applying the function to each artifact directory
|
538
554
|
in the collection.
|
555
|
+
|
539
556
|
"""
|
540
557
|
return (func(dir, *args, **kwargs) for dir in self.info.artifact_dir) # noqa: A001
|
541
558
|
|
@@ -543,8 +560,7 @@ class RunCollection:
|
|
543
560
|
self,
|
544
561
|
*names: str | list[str],
|
545
562
|
) -> dict[tuple[str | None, ...], RunCollection]:
|
546
|
-
"""
|
547
|
-
Group runs by specified parameter names.
|
563
|
+
"""Group runs by specified parameter names.
|
548
564
|
|
549
565
|
Group the runs in the collection based on the values of the
|
550
566
|
specified parameters. Each unique combination of parameter values will
|
@@ -559,6 +575,7 @@ class RunCollection:
|
|
559
575
|
dict[tuple[str | None, ...], RunCollection]: A dictionary where the keys
|
560
576
|
are tuples of parameter values and the values are RunCollection objects
|
561
577
|
containing the runs that match those parameter values.
|
578
|
+
|
562
579
|
"""
|
563
580
|
grouped_runs: dict[tuple[str | None, ...], list[Run]] = {}
|
564
581
|
for run in self._runs:
|
@@ -569,48 +586,25 @@ class RunCollection:
|
|
569
586
|
|
570
587
|
|
571
588
|
def _param_matches(run: Run, key: str, value: Any) -> bool:
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
Check if the run's parameters contain the specified
|
576
|
-
key-value pair. It handles different types of values, including lists
|
577
|
-
and tuples.
|
578
|
-
|
579
|
-
Args:
|
580
|
-
run (Run): The run object to check.
|
581
|
-
key (str): The parameter key to check.
|
582
|
-
value (Any): The parameter value to check.
|
583
|
-
|
584
|
-
Returns:
|
585
|
-
True if the run's parameter matches the specified key-value pair,
|
586
|
-
False otherwise.
|
587
|
-
"""
|
588
|
-
param = run.data.params.get(key, value)
|
589
|
-
|
590
|
-
if param is None:
|
591
|
-
return False
|
589
|
+
params = run.data.params
|
590
|
+
if key not in params:
|
591
|
+
return True
|
592
592
|
|
593
|
+
param = params[key]
|
593
594
|
if param == "None":
|
594
|
-
return value is None
|
595
|
+
return value is None or value == "None"
|
595
596
|
|
596
|
-
|
597
|
-
return type(value[0])(param) in value
|
598
|
-
|
599
|
-
if isinstance(value, tuple) and len(value) == 2: # noqa: PLR2004
|
600
|
-
return value[0] <= type(value[0])(param) < value[1]
|
601
|
-
|
602
|
-
return type(value)(param) == value
|
597
|
+
return hydraflow.param.match(param, value)
|
603
598
|
|
604
599
|
|
605
600
|
def filter_runs(
|
606
601
|
runs: list[Run],
|
607
602
|
config: object | None = None,
|
608
603
|
*,
|
609
|
-
status: str | list[str] | None = None,
|
604
|
+
status: str | list[str] | int | list[int] | None = None,
|
610
605
|
**kwargs,
|
611
606
|
) -> list[Run]:
|
612
|
-
"""
|
613
|
-
Filter the runs based on the provided configuration.
|
607
|
+
"""Filter the runs based on the provided configuration.
|
614
608
|
|
615
609
|
Filter the runs in the collection according to the
|
616
610
|
specified configuration object and additional key-value pairs.
|
@@ -630,33 +624,61 @@ def filter_runs(
|
|
630
624
|
config (object | None): The configuration object to filter the runs.
|
631
625
|
This can be any object that provides key-value pairs through the
|
632
626
|
`iter_params` function.
|
633
|
-
status (str | list[str] | None): The status of
|
627
|
+
status (str | list[str] | RunStatus | list[RunStatus] | None): The status of
|
628
|
+
the runs to filter.
|
634
629
|
**kwargs: Additional key-value pairs to filter the runs.
|
635
630
|
|
636
631
|
Returns:
|
637
632
|
A list of runs that match the specified configuration and key-value pairs.
|
633
|
+
|
638
634
|
"""
|
639
635
|
for key, value in chain(iter_params(config), kwargs.items()):
|
640
636
|
runs = [run for run in runs if _param_matches(run, key, value)]
|
641
637
|
|
642
|
-
|
643
|
-
|
638
|
+
if len(runs) == 0 or status is None:
|
639
|
+
return runs
|
644
640
|
|
645
|
-
|
646
|
-
status = status[1:].lower()
|
647
|
-
return [run for run in runs if run.info.status.lower() != status]
|
641
|
+
return filter_runs_by_status(runs, status)
|
648
642
|
|
649
|
-
if status:
|
650
|
-
status = [status] if isinstance(status, str) else status
|
651
|
-
status = [s.lower() for s in status]
|
652
|
-
return [run for run in runs if run.info.status.lower() in status]
|
653
643
|
|
654
|
-
|
644
|
+
def filter_runs_by_status(
|
645
|
+
runs: list[Run],
|
646
|
+
status: str | list[str] | int | list[int],
|
647
|
+
) -> list[Run]:
|
648
|
+
"""Filter the runs based on the provided status.
|
655
649
|
|
650
|
+
Args:
|
651
|
+
runs (list[Run]): The list of runs to filter.
|
652
|
+
status (str | list[str] | int | list[int]): The status of the runs
|
653
|
+
to filter.
|
654
|
+
|
655
|
+
Returns:
|
656
|
+
A list of runs that match the specified status.
|
656
657
|
|
657
|
-
def get_params(run: Run, *names: str | list[str]) -> tuple[str | None, ...]:
|
658
658
|
"""
|
659
|
-
|
659
|
+
if isinstance(status, str):
|
660
|
+
if status.startswith("!"):
|
661
|
+
status = status[1:].lower()
|
662
|
+
return [run for run in runs if run.info.status.lower() != status]
|
663
|
+
|
664
|
+
status = [status]
|
665
|
+
|
666
|
+
elif isinstance(status, int):
|
667
|
+
status = [RunStatus.to_string(status)]
|
668
|
+
|
669
|
+
status = [_to_lower(s) for s in status]
|
670
|
+
return [run for run in runs if run.info.status.lower() in status]
|
671
|
+
|
672
|
+
|
673
|
+
def _to_lower(status: str | int) -> str:
|
674
|
+
if isinstance(status, str):
|
675
|
+
return status.lower()
|
676
|
+
|
677
|
+
return RunStatus.to_string(status).lower()
|
678
|
+
|
679
|
+
|
680
|
+
def get_params(run: Run, *names: str | list[str]) -> tuple[str | None, ...]:
|
681
|
+
"""Retrieve the values of specified parameters from the given run.
|
660
682
|
|
661
683
|
This function extracts the values of the parameters identified by the
|
662
684
|
provided names from the specified run. It can accept both individual
|
@@ -671,6 +693,7 @@ def get_params(run: Run, *names: str | list[str]) -> tuple[str | None, ...]:
|
|
671
693
|
Returns:
|
672
694
|
tuple[str | None, ...]: A tuple containing the values of the specified
|
673
695
|
parameters in the order they were provided.
|
696
|
+
|
674
697
|
"""
|
675
698
|
names_ = []
|
676
699
|
for name in names:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: hydraflow
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.18
|
4
4
|
Summary: Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments.
|
5
5
|
Project-URL: Documentation, https://github.com/daizutabi/hydraflow
|
6
6
|
Project-URL: Source, https://github.com/daizutabi/hydraflow
|
@@ -0,0 +1,14 @@
|
|
1
|
+
hydraflow/__init__.py,sha256=B7rWSiGP5WwWjijcb41Bv9uuo5MQ6gbBbVWGAWYtK-k,598
|
2
|
+
hydraflow/asyncio.py,sha256=-i1C8KAmNDImrjHnk92Csaa1mpjdK8Vp4ZVaQV-l94s,6634
|
3
|
+
hydraflow/config.py,sha256=sBaEYPMAGSIOc_wdDsWm0k4y3AZyWIET8gqa_o95SDA,2089
|
4
|
+
hydraflow/context.py,sha256=ih_jnexaHoToNq1dZ6sBzhJWFluPiQluOlYTYOzNEgk,8222
|
5
|
+
hydraflow/info.py,sha256=Vzyz9dEWcU9ovRG3JWshxIazzod1cZoHF74bHhHL3AI,3946
|
6
|
+
hydraflow/mlflow.py,sha256=GkOr_pXfpfY5USYBLrCigHcP13VgrAK_e9kheR1Wke4,8579
|
7
|
+
hydraflow/param.py,sha256=dvIXcKgc_MPiju3WEk9qz5FOUeK5qSj-YWN2ophCpUM,1938
|
8
|
+
hydraflow/progress.py,sha256=zvKX1HCN8_xDOsgYOEcLLhkhdPdep-U8vHrc0XZ-6SQ,6163
|
9
|
+
hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
hydraflow/run_collection.py,sha256=gsseBQ6a2YolNanISgEgkjei7o9U6ZGV-Tk50UYH850,24295
|
11
|
+
hydraflow-0.2.18.dist-info/METADATA,sha256=roL3lGtlIibF6rHbCp4aXrCphhq-OkNe0JwLxM1xtBY,3819
|
12
|
+
hydraflow-0.2.18.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
13
|
+
hydraflow-0.2.18.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
14
|
+
hydraflow-0.2.18.dist-info/RECORD,,
|
@@ -1,13 +0,0 @@
|
|
1
|
-
hydraflow/__init__.py,sha256=B7rWSiGP5WwWjijcb41Bv9uuo5MQ6gbBbVWGAWYtK-k,598
|
2
|
-
hydraflow/asyncio.py,sha256=eFnDbNOQ5Hmjdforr8rTW6i_rr-zFIVY3xSQQ45gMPA,6511
|
3
|
-
hydraflow/config.py,sha256=YU6xYLinxq-Iqw1R3Zy7s3_u8nfpvnvXlGIkPXJTNLc,2116
|
4
|
-
hydraflow/context.py,sha256=4UDaWGoVmeF36UqsKoh6dd_cS_YVRfz80gFr28ouNlo,8040
|
5
|
-
hydraflow/info.py,sha256=7EsCMEH6LJZB3FZiQ3IpPFTD3Meaz7G3M-HvDQeo1rw,3466
|
6
|
-
hydraflow/mlflow.py,sha256=irD1INrVaI_1RIzUCjI36voBqgZszZ4dkSLo4aT1_FM,8271
|
7
|
-
hydraflow/progress.py,sha256=b5LvLm3d0eW3WsaidZAZotJNTTN3OwSY3XwxXXsJV9A,6561
|
8
|
-
hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
hydraflow/run_collection.py,sha256=tiNKy_mUmE-9moLs7enfLQyiwTUvL5eCKnD1acKeUFw,23854
|
10
|
-
hydraflow-0.2.16.dist-info/METADATA,sha256=PUsFQ8YLW_L-rVzzx1OzQX6imjdQglhIAgJCSV9qEaM,3819
|
11
|
-
hydraflow-0.2.16.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
12
|
-
hydraflow-0.2.16.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
13
|
-
hydraflow-0.2.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|