hydraflow 0.2.16__py3-none-any.whl → 0.2.18__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|