hydraflow 0.2.2__py3-none-any.whl → 0.2.3__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/__init__.py +2 -0
- hydraflow/asyncio.py +199 -0
- hydraflow/runs.py +377 -134
- {hydraflow-0.2.2.dist-info → hydraflow-0.2.3.dist-info}/METADATA +3 -1
- hydraflow-0.2.3.dist-info/RECORD +10 -0
- hydraflow-0.2.2.dist-info/RECORD +0 -9
- {hydraflow-0.2.2.dist-info → hydraflow-0.2.3.dist-info}/WHEEL +0 -0
- {hydraflow-0.2.2.dist-info → hydraflow-0.2.3.dist-info}/licenses/LICENSE +0 -0
hydraflow/__init__.py
CHANGED
@@ -7,6 +7,7 @@ from .runs import (
|
|
7
7
|
get_param_dict,
|
8
8
|
get_param_names,
|
9
9
|
get_run,
|
10
|
+
list_runs,
|
10
11
|
load_config,
|
11
12
|
search_runs,
|
12
13
|
)
|
@@ -20,6 +21,7 @@ __all__ = [
|
|
20
21
|
"get_param_dict",
|
21
22
|
"get_param_names",
|
22
23
|
"get_run",
|
24
|
+
"list_runs",
|
23
25
|
"load_config",
|
24
26
|
"log_run",
|
25
27
|
"search_runs",
|
hydraflow/asyncio.py
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import logging
|
5
|
+
from asyncio.subprocess import PIPE
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import TYPE_CHECKING
|
8
|
+
|
9
|
+
import watchfiles
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from asyncio.streams import StreamReader
|
13
|
+
from collections.abc import Callable
|
14
|
+
|
15
|
+
from watchfiles import Change
|
16
|
+
|
17
|
+
|
18
|
+
# Set up logging
|
19
|
+
logging.basicConfig(level=logging.INFO)
|
20
|
+
logger = logging.getLogger(__name__)
|
21
|
+
|
22
|
+
|
23
|
+
async def execute_command(
|
24
|
+
program: str,
|
25
|
+
*args: str,
|
26
|
+
stdout: Callable[[str], None] | None = None,
|
27
|
+
stderr: Callable[[str], None] | None = None,
|
28
|
+
stop_event: asyncio.Event,
|
29
|
+
) -> int:
|
30
|
+
"""
|
31
|
+
Runs a command asynchronously and pass the output to callback functions.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
program (str): The program to run.
|
35
|
+
*args (str): Arguments for the program.
|
36
|
+
stdout (Callable[[str], None] | None): Callback for standard output.
|
37
|
+
stderr (Callable[[str], None] | None): Callback for standard error.
|
38
|
+
stop_event (asyncio.Event): Event to signal when the process is done.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
int: The return code of the process.
|
42
|
+
"""
|
43
|
+
try:
|
44
|
+
process = await asyncio.create_subprocess_exec(program, *args, stdout=PIPE, stderr=PIPE)
|
45
|
+
await asyncio.gather(
|
46
|
+
process_stream(process.stdout, stdout),
|
47
|
+
process_stream(process.stderr, stderr),
|
48
|
+
)
|
49
|
+
returncode = await process.wait()
|
50
|
+
|
51
|
+
except Exception as e:
|
52
|
+
logger.error(f"Error running command: {e}")
|
53
|
+
returncode = 1
|
54
|
+
|
55
|
+
finally:
|
56
|
+
stop_event.set()
|
57
|
+
|
58
|
+
return returncode
|
59
|
+
|
60
|
+
|
61
|
+
async def process_stream(
|
62
|
+
stream: StreamReader | None,
|
63
|
+
callback: Callable[[str], None] | None,
|
64
|
+
) -> None:
|
65
|
+
"""
|
66
|
+
Reads a stream asynchronously and pass each line to a callback function.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
stream (StreamReader | None): The stream to read from.
|
70
|
+
callback (Callable[[str], None] | None): The callback function to handle
|
71
|
+
each line.
|
72
|
+
"""
|
73
|
+
if stream is None or callback is None:
|
74
|
+
return
|
75
|
+
|
76
|
+
while True:
|
77
|
+
line = await stream.readline()
|
78
|
+
if line:
|
79
|
+
callback(line.decode().strip())
|
80
|
+
else:
|
81
|
+
break
|
82
|
+
|
83
|
+
|
84
|
+
async def monitor_file_changes(
|
85
|
+
paths: list[str | Path],
|
86
|
+
callback: Callable[[set[tuple[Change, str]]], None],
|
87
|
+
stop_event: asyncio.Event,
|
88
|
+
**awatch_kwargs,
|
89
|
+
) -> None:
|
90
|
+
"""
|
91
|
+
Watches for file changes in specified paths and pass the changes to a
|
92
|
+
callback function.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
paths (list[str | Path]): List of paths to monitor for changes.
|
96
|
+
callback (Callable[[set[tuple[Change, str]]], None]): The callback
|
97
|
+
function to handle file changes.
|
98
|
+
stop_event (asyncio.Event): Event to signal when to stop watching.
|
99
|
+
**awatch_kwargs: Additional keyword arguments to pass to watchfiles.awatch.
|
100
|
+
"""
|
101
|
+
str_paths = [str(path) for path in paths]
|
102
|
+
try:
|
103
|
+
async for changes in watchfiles.awatch(*str_paths, stop_event=stop_event, **awatch_kwargs):
|
104
|
+
callback(changes)
|
105
|
+
except Exception as e:
|
106
|
+
logger.error(f"Error watching files: {e}")
|
107
|
+
|
108
|
+
|
109
|
+
async def run_and_monitor(
|
110
|
+
program: str,
|
111
|
+
*args: str,
|
112
|
+
stdout: Callable[[str], None] | None = None,
|
113
|
+
stderr: Callable[[str], None] | None = None,
|
114
|
+
watch: Callable[[set[tuple[Change, str]]], None] | None = None,
|
115
|
+
paths: list[str | Path] | None = None,
|
116
|
+
**awatch_kwargs,
|
117
|
+
) -> int:
|
118
|
+
"""
|
119
|
+
Runs a command and optionally watch for file changes concurrently.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
program (str): The program to run.
|
123
|
+
*args (str): Arguments for the program.
|
124
|
+
stdout (Callable[[str], None] | None): Callback for standard output.
|
125
|
+
stderr (Callable[[str], None] | None): Callback for standard error.
|
126
|
+
watch (Callable[[set[tuple[Change, str]]], None] | None): Callback for
|
127
|
+
file changes.
|
128
|
+
paths (list[str | Path] | None): List of paths to monitor for changes.
|
129
|
+
"""
|
130
|
+
stop_event = asyncio.Event()
|
131
|
+
run_task = asyncio.create_task(
|
132
|
+
execute_command(program, *args, stop_event=stop_event, stdout=stdout, stderr=stderr)
|
133
|
+
)
|
134
|
+
if watch and paths:
|
135
|
+
monitor_task = asyncio.create_task(
|
136
|
+
monitor_file_changes(paths, watch, stop_event, **awatch_kwargs)
|
137
|
+
)
|
138
|
+
else:
|
139
|
+
monitor_task = None
|
140
|
+
|
141
|
+
try:
|
142
|
+
if monitor_task:
|
143
|
+
await asyncio.gather(run_task, monitor_task)
|
144
|
+
else:
|
145
|
+
await run_task
|
146
|
+
|
147
|
+
except Exception as e:
|
148
|
+
logger.error(f"Error in run_and_monitor: {e}")
|
149
|
+
finally:
|
150
|
+
stop_event.set()
|
151
|
+
await run_task
|
152
|
+
if monitor_task:
|
153
|
+
await monitor_task
|
154
|
+
|
155
|
+
return run_task.result()
|
156
|
+
|
157
|
+
|
158
|
+
def run(
|
159
|
+
program: str,
|
160
|
+
*args: str,
|
161
|
+
stdout: Callable[[str], None] | None = None,
|
162
|
+
stderr: Callable[[str], None] | None = None,
|
163
|
+
watch: Callable[[set[tuple[Change, str]]], None] | None = None,
|
164
|
+
paths: list[str | Path] | None = None,
|
165
|
+
**awatch_kwargs,
|
166
|
+
) -> int:
|
167
|
+
"""
|
168
|
+
Run a command synchronously and optionally watch for file changes.
|
169
|
+
|
170
|
+
This function is a synchronous wrapper around the asynchronous `run_and_monitor` function.
|
171
|
+
It runs a specified command and optionally monitors specified paths for file changes,
|
172
|
+
invoking the provided callbacks for standard output, standard error, and file changes.
|
173
|
+
|
174
|
+
Args:
|
175
|
+
program (str): The program to run.
|
176
|
+
*args (str): Arguments for the program.
|
177
|
+
stdout (Callable[[str], None] | None): Callback for handling standard output lines.
|
178
|
+
stderr (Callable[[str], None] | None): Callback for handling standard error lines.
|
179
|
+
watch (Callable[[set[tuple[Change, str]]], None] | None): Callback for handling file changes.
|
180
|
+
paths (list[str | Path] | None): List of paths to monitor for file changes.
|
181
|
+
**awatch_kwargs: Additional keyword arguments to pass to `watchfiles.awatch`.
|
182
|
+
|
183
|
+
Returns:
|
184
|
+
int: The return code of the process.
|
185
|
+
"""
|
186
|
+
if watch and not paths:
|
187
|
+
paths = [Path.cwd()]
|
188
|
+
|
189
|
+
return asyncio.run(
|
190
|
+
run_and_monitor(
|
191
|
+
program,
|
192
|
+
*args,
|
193
|
+
stdout=stdout,
|
194
|
+
stderr=stderr,
|
195
|
+
watch=watch,
|
196
|
+
paths=paths,
|
197
|
+
**awatch_kwargs,
|
198
|
+
)
|
199
|
+
)
|
hydraflow/runs.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""
|
2
|
-
This module provides functionality for managing and interacting with MLflow
|
3
|
-
It includes the `RunCollection` class and various methods to filter
|
4
|
-
retrieve run information, log artifacts, and load configurations.
|
2
|
+
This module provides functionality for managing and interacting with MLflow
|
3
|
+
runs. It includes the `RunCollection` class and various methods to filter
|
4
|
+
runs, retrieve run information, log artifacts, and load configurations.
|
5
5
|
"""
|
6
6
|
|
7
7
|
from __future__ import annotations
|
@@ -37,34 +37,39 @@ def search_runs(
|
|
37
37
|
"""
|
38
38
|
Search for Runs that fit the specified criteria.
|
39
39
|
|
40
|
-
This function wraps the `mlflow.search_runs` function and returns the
|
41
|
-
as a `RunCollection` object. It allows for flexible searching of
|
42
|
-
various criteria.
|
40
|
+
This function wraps the `mlflow.search_runs` function and returns the
|
41
|
+
results as a `RunCollection` object. It allows for flexible searching of
|
42
|
+
MLflow runs based on various criteria.
|
43
43
|
|
44
44
|
Note:
|
45
45
|
The returned runs are sorted by their start time in ascending order.
|
46
46
|
|
47
47
|
Args:
|
48
|
-
experiment_ids: List of experiment IDs. Search can work with experiment
|
49
|
-
experiment names, but not both in the same call. Values
|
50
|
-
``None`` or ``[]`` will result in error if
|
51
|
-
also not ``None`` or ``[]``. ``None`` will
|
52
|
-
experiment if ``experiment_names`` is ``None``
|
48
|
+
experiment_ids: List of experiment IDs. Search can work with experiment
|
49
|
+
IDs or experiment names, but not both in the same call. Values
|
50
|
+
other than ``None`` or ``[]`` will result in error if
|
51
|
+
``experiment_names`` is also not ``None`` or ``[]``. ``None`` will
|
52
|
+
default to the active experiment if ``experiment_names`` is ``None``
|
53
|
+
or ``[]``.
|
53
54
|
filter_string: Filter query string, defaults to searching all runs.
|
54
|
-
run_view_type: one of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``, or
|
55
|
-
defined in :py:class:`mlflow.entities.ViewType`.
|
56
|
-
max_results: The maximum number of runs to put in the dataframe. Default
|
57
|
-
to avoid causing out-of-memory issues on the user's
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
experiment
|
55
|
+
run_view_type: one of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``, or
|
56
|
+
``ALL`` runs defined in :py:class:`mlflow.entities.ViewType`.
|
57
|
+
max_results: The maximum number of runs to put in the dataframe. Default
|
58
|
+
is 100,000 to avoid causing out-of-memory issues on the user's
|
59
|
+
machine.
|
60
|
+
order_by: List of columns to order by (e.g., "metrics.rmse"). The
|
61
|
+
``order_by`` column can contain an optional ``DESC`` or ``ASC``
|
62
|
+
value. The default is ``ASC``. The default ordering is to sort by
|
63
|
+
``start_time DESC``, then ``run_id``.
|
64
|
+
search_all_experiments: Boolean specifying whether all experiments
|
65
|
+
should be searched. Only honored if ``experiment_ids`` is ``[]`` or
|
66
|
+
``None``.
|
67
|
+
experiment_names: List of experiment names. Search can work with
|
68
|
+
experiment IDs or experiment names, but not both in the same call.
|
69
|
+
Values other than ``None`` or ``[]`` will result in error if
|
70
|
+
``experiment_ids`` is also not ``None`` or ``[]``. ``None`` will
|
71
|
+
default to the active experiment if ``experiment_ids`` is ``None``
|
72
|
+
or ``[]``.
|
68
73
|
|
69
74
|
Returns:
|
70
75
|
A `RunCollection` object containing the search results.
|
@@ -133,27 +138,77 @@ class RunCollection:
|
|
133
138
|
def __len__(self) -> int:
|
134
139
|
return len(self._runs)
|
135
140
|
|
141
|
+
def first(self) -> Run:
|
142
|
+
"""
|
143
|
+
Get the first run in the collection.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
The first run object in the collection.
|
147
|
+
|
148
|
+
Raises:
|
149
|
+
ValueError: If the collection is empty.
|
150
|
+
"""
|
151
|
+
if not self._runs:
|
152
|
+
raise ValueError("The collection is empty.")
|
153
|
+
|
154
|
+
return self._runs[0]
|
155
|
+
|
156
|
+
def try_first(self) -> Run | None:
|
157
|
+
"""
|
158
|
+
Try to get the first run in the collection.
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
The first run object in the collection, or None if the collection
|
162
|
+
is empty.
|
163
|
+
"""
|
164
|
+
return self._runs[0] if self._runs else None
|
165
|
+
|
166
|
+
def last(self) -> Run:
|
167
|
+
"""
|
168
|
+
Get the last run in the collection.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
The last run object in the collection.
|
172
|
+
|
173
|
+
Raises:
|
174
|
+
ValueError: If the collection is empty.
|
175
|
+
"""
|
176
|
+
if not self._runs:
|
177
|
+
raise ValueError("The collection is empty.")
|
178
|
+
|
179
|
+
return self._runs[-1]
|
180
|
+
|
181
|
+
def try_last(self) -> Run | None:
|
182
|
+
"""
|
183
|
+
Try to get the last run in the collection.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
The last run object in the collection, or None if the collection is
|
187
|
+
empty.
|
188
|
+
"""
|
189
|
+
return self._runs[-1] if self._runs else None
|
190
|
+
|
136
191
|
def filter(self, config: object | None = None, **kwargs) -> RunCollection:
|
137
192
|
"""
|
138
193
|
Filter the runs based on the provided configuration.
|
139
194
|
|
140
195
|
This method filters the runs in the collection according to the
|
141
|
-
specified configuration object and additional key-value pairs.
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
196
|
+
specified configuration object and additional key-value pairs. The
|
197
|
+
configuration object and key-value pairs should contain key-value pairs
|
198
|
+
that correspond to the parameters of the runs. Only the runs that match
|
199
|
+
all the specified parameters will be included in the returned
|
200
|
+
`RunCollection` object.
|
146
201
|
|
147
202
|
The filtering supports:
|
148
203
|
- Exact matches for single values.
|
149
204
|
- Membership checks for lists of values.
|
150
|
-
- Range checks for tuples of two values (inclusive of the lower bound
|
151
|
-
exclusive of the upper bound).
|
205
|
+
- Range checks for tuples of two values (inclusive of the lower bound
|
206
|
+
and exclusive of the upper bound).
|
152
207
|
|
153
208
|
Args:
|
154
|
-
config: The configuration object to filter the runs. This can be
|
155
|
-
object that provides key-value pairs through the
|
156
|
-
function.
|
209
|
+
config: The configuration object to filter the runs. This can be
|
210
|
+
any object that provides key-value pairs through the
|
211
|
+
`iter_params` function.
|
157
212
|
**kwargs: Additional key-value pairs to filter the runs.
|
158
213
|
|
159
214
|
Returns:
|
@@ -161,80 +216,161 @@ class RunCollection:
|
|
161
216
|
"""
|
162
217
|
return RunCollection(filter_runs(self._runs, config, **kwargs))
|
163
218
|
|
164
|
-
def find(self, config: object | None = None, **kwargs) -> Run
|
219
|
+
def find(self, config: object | None = None, **kwargs) -> Run:
|
165
220
|
"""
|
166
221
|
Find the first run based on the provided configuration.
|
167
222
|
|
168
223
|
This method filters the runs in the collection according to the
|
169
224
|
specified configuration object and returns the first run that matches
|
170
|
-
the provided parameters. If no run matches the criteria,
|
225
|
+
the provided parameters. If no run matches the criteria, a `ValueError`
|
226
|
+
is raised.
|
171
227
|
|
172
228
|
Args:
|
173
229
|
config: The configuration object to identify the run.
|
174
230
|
**kwargs: Additional key-value pairs to filter the runs.
|
175
231
|
|
176
232
|
Returns:
|
177
|
-
The first run object that matches the provided configuration
|
178
|
-
|
233
|
+
The first run object that matches the provided configuration.
|
234
|
+
|
235
|
+
Raises:
|
236
|
+
ValueError: If no run matches the criteria.
|
179
237
|
|
180
238
|
See Also:
|
181
|
-
RunCollection.filter: The method that performs the actual filtering
|
239
|
+
RunCollection.filter: The method that performs the actual filtering
|
240
|
+
logic.
|
182
241
|
"""
|
183
242
|
return find_run(self._runs, config, **kwargs)
|
184
243
|
|
185
|
-
def
|
244
|
+
def try_find(self, config: object | None = None, **kwargs) -> Run | None:
|
245
|
+
"""
|
246
|
+
Find the first run based on the provided configuration.
|
247
|
+
|
248
|
+
This method filters the runs in the collection according to the
|
249
|
+
specified configuration object and returns the first run that matches
|
250
|
+
the provided parameters. If no run matches the criteria, None is
|
251
|
+
returned.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
config: The configuration object to identify the run.
|
255
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
256
|
+
|
257
|
+
Returns:
|
258
|
+
The first run object that matches the provided configuration, or
|
259
|
+
None if no runs match the criteria.
|
260
|
+
|
261
|
+
See Also:
|
262
|
+
RunCollection.filter: The method that performs the actual filtering
|
263
|
+
logic.
|
264
|
+
"""
|
265
|
+
return try_find_run(self._runs, config, **kwargs)
|
266
|
+
|
267
|
+
def find_last(self, config: object | None = None, **kwargs) -> Run:
|
186
268
|
"""
|
187
269
|
Find the last run based on the provided configuration.
|
188
270
|
|
189
271
|
This method filters the runs in the collection according to the
|
190
272
|
specified configuration object and returns the last run that matches
|
191
|
-
the provided parameters. If no run matches the criteria,
|
273
|
+
the provided parameters. If no run matches the criteria, a `ValueError`
|
274
|
+
is raised.
|
192
275
|
|
193
276
|
Args:
|
194
277
|
config: The configuration object to identify the run.
|
195
278
|
**kwargs: Additional key-value pairs to filter the runs.
|
196
279
|
|
197
280
|
Returns:
|
198
|
-
The last run object that matches the provided configuration
|
199
|
-
|
281
|
+
The last run object that matches the provided configuration.
|
282
|
+
|
283
|
+
Raises:
|
284
|
+
ValueError: If no run matches the criteria.
|
200
285
|
|
201
286
|
See Also:
|
202
|
-
RunCollection.filter: The method that performs the actual filtering
|
287
|
+
RunCollection.filter: The method that performs the actual filtering
|
288
|
+
logic.
|
203
289
|
"""
|
204
290
|
return find_last_run(self._runs, config, **kwargs)
|
205
291
|
|
206
|
-
def
|
292
|
+
def try_find_last(self, config: object | None = None, **kwargs) -> Run | None:
|
293
|
+
"""
|
294
|
+
Find the last run based on the provided configuration.
|
295
|
+
|
296
|
+
This method filters the runs in the collection according to the
|
297
|
+
specified configuration object and returns the last run that matches
|
298
|
+
the provided parameters. If no run matches the criteria, None is
|
299
|
+
returned.
|
300
|
+
|
301
|
+
Args:
|
302
|
+
config: The configuration object to identify the run.
|
303
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
304
|
+
|
305
|
+
Returns:
|
306
|
+
The last run object that matches the provided configuration, or
|
307
|
+
None if no runs match the criteria.
|
308
|
+
|
309
|
+
See Also:
|
310
|
+
RunCollection.filter: The method that performs the actual filtering
|
311
|
+
logic.
|
312
|
+
"""
|
313
|
+
return try_find_last_run(self._runs, config, **kwargs)
|
314
|
+
|
315
|
+
def get(self, config: object | None = None, **kwargs) -> Run:
|
207
316
|
"""
|
208
317
|
Retrieve a specific run based on the provided configuration.
|
209
318
|
|
210
319
|
This method filters the runs in the collection according to the
|
211
|
-
specified configuration object and returns the run that matches
|
212
|
-
|
213
|
-
a `ValueError` is raised.
|
320
|
+
specified configuration object and returns the run that matches the
|
321
|
+
provided parameters. If no run matches the criteria, or if more than
|
322
|
+
one run matches the criteria, a `ValueError` is raised.
|
214
323
|
|
215
324
|
Args:
|
216
325
|
config: The configuration object to identify the run.
|
217
326
|
**kwargs: Additional key-value pairs to filter the runs.
|
218
327
|
|
219
328
|
Returns:
|
220
|
-
The run object that matches the provided configuration
|
221
|
-
if no runs match the criteria.
|
329
|
+
The run object that matches the provided configuration.
|
222
330
|
|
223
331
|
Raises:
|
224
|
-
ValueError: If
|
332
|
+
ValueError: If no run matches the criteria or if more than one run
|
333
|
+
matches the criteria.
|
225
334
|
|
226
335
|
See Also:
|
227
|
-
RunCollection.filter: The method that performs the actual filtering
|
336
|
+
RunCollection.filter: The method that performs the actual filtering
|
337
|
+
logic.
|
228
338
|
"""
|
229
339
|
return get_run(self._runs, config, **kwargs)
|
230
340
|
|
341
|
+
def try_get(self, config: object | None = None, **kwargs) -> Run | None:
|
342
|
+
"""
|
343
|
+
Retrieve a specific run based on the provided configuration.
|
344
|
+
|
345
|
+
This method filters the runs in the collection according to the
|
346
|
+
specified configuration object and returns the run that matches the
|
347
|
+
provided parameters. If no run matches the criteria, None is returned.
|
348
|
+
If more than one run matches the criteria, a `ValueError` is raised.
|
349
|
+
|
350
|
+
Args:
|
351
|
+
config: The configuration object to identify the run.
|
352
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
353
|
+
|
354
|
+
Returns:
|
355
|
+
The run object that matches the provided configuration, or None if
|
356
|
+
no runs match the criteria.
|
357
|
+
|
358
|
+
Raises:
|
359
|
+
ValueError: If more than one run matches the criteria.
|
360
|
+
|
361
|
+
See Also:
|
362
|
+
RunCollection.filter: The method that performs the actual filtering
|
363
|
+
logic.
|
364
|
+
"""
|
365
|
+
return try_get_run(self._runs, config, **kwargs)
|
366
|
+
|
231
367
|
def get_param_names(self) -> list[str]:
|
232
368
|
"""
|
233
369
|
Get the parameter names from the runs.
|
234
370
|
|
235
|
-
This method extracts the unique parameter names from the provided list
|
236
|
-
It iterates through each run and collects the parameter names
|
237
|
-
ensure uniqueness.
|
371
|
+
This method extracts the unique parameter names from the provided list
|
372
|
+
of runs. It iterates through each run and collects the parameter names
|
373
|
+
into a set to ensure uniqueness.
|
238
374
|
|
239
375
|
Returns:
|
240
376
|
A list of unique parameter names.
|
@@ -246,101 +382,93 @@ class RunCollection:
|
|
246
382
|
Get the parameter dictionary from the list of runs.
|
247
383
|
|
248
384
|
This method extracts the parameter names and their corresponding values
|
249
|
-
from the provided list of runs. It iterates through each run and
|
250
|
-
the parameter values into a dictionary where the keys are
|
251
|
-
and the values are lists of parameter values.
|
385
|
+
from the provided list of runs. It iterates through each run and
|
386
|
+
collects the parameter values into a dictionary where the keys are
|
387
|
+
parameter names and the values are lists of parameter values.
|
252
388
|
|
253
389
|
Returns:
|
254
|
-
A dictionary where the keys are parameter names and the values are
|
255
|
-
of parameter values.
|
390
|
+
A dictionary where the keys are parameter names and the values are
|
391
|
+
lists of parameter values.
|
256
392
|
"""
|
257
393
|
return get_param_dict(self._runs)
|
258
394
|
|
259
|
-
def first(self) -> Run | None:
|
260
|
-
"""
|
261
|
-
Return the first run in the collection.
|
262
|
-
|
263
|
-
Returns:
|
264
|
-
The first Run object if the collection is not empty, otherwise None.
|
265
|
-
"""
|
266
|
-
return self._runs[0] if self._runs else None
|
267
|
-
|
268
|
-
def last(self) -> Run | None:
|
269
|
-
"""
|
270
|
-
Return the last run in the collection.
|
271
|
-
|
272
|
-
Returns:
|
273
|
-
The last Run object if the collection is not empty, otherwise None.
|
274
|
-
"""
|
275
|
-
return self._runs[-1] if self._runs else None
|
276
|
-
|
277
395
|
def map(self, func: Callable[[Run], T]) -> Iterator[T]:
|
278
396
|
"""
|
279
|
-
Apply a function to each run in the collection and return an iterator of
|
397
|
+
Apply a function to each run in the collection and return an iterator of
|
398
|
+
results.
|
280
399
|
|
281
400
|
Args:
|
282
|
-
func: A function that takes a
|
401
|
+
func: A function that takes a run and returns a result.
|
283
402
|
|
284
|
-
|
285
|
-
|
286
|
-
|
403
|
+
Yields:
|
404
|
+
Results obtained by applying the function to each run in the
|
405
|
+
collection.
|
287
406
|
"""
|
288
407
|
return (func(run) for run in self._runs)
|
289
408
|
|
290
409
|
def map_run_id(self, func: Callable[[str], T]) -> Iterator[T]:
|
291
410
|
"""
|
292
|
-
Apply a function to each run id in the collection and return an iterator
|
411
|
+
Apply a function to each run id in the collection and return an iterator
|
412
|
+
of results.
|
293
413
|
|
294
414
|
Args:
|
295
415
|
func: A function that takes a run id and returns a result.
|
296
416
|
|
297
|
-
|
298
|
-
|
299
|
-
|
417
|
+
Yields:
|
418
|
+
Results obtained by applying the function to each run id in the
|
419
|
+
collection.
|
300
420
|
"""
|
301
421
|
return (func(run.info.run_id) for run in self._runs)
|
302
422
|
|
303
423
|
def map_config(self, func: Callable[[DictConfig], T]) -> Iterator[T]:
|
304
424
|
"""
|
305
|
-
Apply a function to each run
|
425
|
+
Apply a function to each run configuration in the collection and return
|
426
|
+
an iterator of results.
|
306
427
|
|
307
428
|
Args:
|
308
|
-
func: A function that takes a run
|
429
|
+
func: A function that takes a run configuration and returns a
|
430
|
+
result.
|
309
431
|
|
310
|
-
|
311
|
-
|
432
|
+
Yields:
|
433
|
+
Results obtained by applying the function to each run configuration
|
312
434
|
in the collection.
|
313
435
|
"""
|
314
436
|
return (func(load_config(run)) for run in self._runs)
|
315
437
|
|
316
438
|
def map_uri(self, func: Callable[[str | None], T]) -> Iterator[T]:
|
317
439
|
"""
|
318
|
-
Apply a function to each artifact URI in the collection and return an
|
440
|
+
Apply a function to each artifact URI in the collection and return an
|
441
|
+
iterator of results.
|
319
442
|
|
320
|
-
This method iterates over each run in the collection, retrieves the
|
321
|
-
and applies the provided function to it. If a run does not
|
322
|
-
None is passed to the function.
|
443
|
+
This method iterates over each run in the collection, retrieves the
|
444
|
+
artifact URI, and applies the provided function to it. If a run does not
|
445
|
+
have an artifact URI, None is passed to the function.
|
323
446
|
|
324
447
|
Args:
|
325
|
-
func: A function that takes an artifact URI (string or None) and
|
448
|
+
func: A function that takes an artifact URI (string or None) and
|
449
|
+
returns a result.
|
326
450
|
|
327
451
|
Yields:
|
328
|
-
|
452
|
+
Results obtained by applying the function to each artifact URI in the
|
453
|
+
collection.
|
329
454
|
"""
|
330
455
|
return (func(run.info.artifact_uri) for run in self._runs)
|
331
456
|
|
332
457
|
def map_dir(self, func: Callable[[str], T]) -> Iterator[T]:
|
333
458
|
"""
|
334
|
-
Apply a function to each artifact directory in the collection and return
|
459
|
+
Apply a function to each artifact directory in the collection and return
|
460
|
+
an iterator of results.
|
335
461
|
|
336
|
-
This method iterates over each run in the collection, downloads the
|
337
|
-
and applies the provided function to the directory
|
462
|
+
This method iterates over each run in the collection, downloads the
|
463
|
+
artifact directory, and applies the provided function to the directory
|
464
|
+
path.
|
338
465
|
|
339
466
|
Args:
|
340
|
-
func: A function that takes an artifact directory path (string) and
|
467
|
+
func: A function that takes an artifact directory path (string) and
|
468
|
+
returns a result.
|
341
469
|
|
342
|
-
|
343
|
-
|
470
|
+
Yields:
|
471
|
+
Results obtained by applying the function to each artifact directory
|
344
472
|
in the collection.
|
345
473
|
"""
|
346
474
|
return (func(download_artifacts(run_id=run.info.run_id)) for run in self._runs)
|
@@ -350,8 +478,9 @@ def _param_matches(run: Run, key: str, value: Any) -> bool:
|
|
350
478
|
"""
|
351
479
|
Check if the run's parameter matches the specified key-value pair.
|
352
480
|
|
353
|
-
This function checks if the run's parameters contain the specified
|
354
|
-
It handles different types of values, including lists
|
481
|
+
This function checks if the run's parameters contain the specified
|
482
|
+
key-value pair. It handles different types of values, including lists
|
483
|
+
and tuples.
|
355
484
|
|
356
485
|
Args:
|
357
486
|
run: The run object to check.
|
@@ -359,16 +488,11 @@ def _param_matches(run: Run, key: str, value: Any) -> bool:
|
|
359
488
|
value: The parameter value to check.
|
360
489
|
|
361
490
|
Returns:
|
362
|
-
True if the run's parameter matches the specified key-value pair,
|
491
|
+
True if the run's parameter matches the specified key-value pair,
|
492
|
+
False otherwise.
|
363
493
|
"""
|
364
494
|
param = run.data.params.get(key, value)
|
365
495
|
|
366
|
-
# FIXME: This is a workaround to handle the case where the parameter value is a list
|
367
|
-
# We need to improve the logic to handle different types of values
|
368
|
-
# For now, we assume that if the parameter is a list, we should check if it contains the value
|
369
|
-
# This is not ideal, but it works for the case where the parameter value is a list of strings
|
370
|
-
# We should improve the logic to handle different types of values in the future
|
371
|
-
|
372
496
|
if param is None:
|
373
497
|
return False
|
374
498
|
|
@@ -392,8 +516,8 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
|
|
392
516
|
specified configuration object and additional key-value pairs.
|
393
517
|
The configuration object and key-value pairs should contain
|
394
518
|
key-value pairs that correspond to the parameters of the runs.
|
395
|
-
Only the runs that match all the specified parameters will
|
396
|
-
included in the returned list of runs.
|
519
|
+
Only the runs that match all the specified parameters will
|
520
|
+
be included in the returned list of runs.
|
397
521
|
|
398
522
|
The filtering supports:
|
399
523
|
- Exact matches for single values.
|
@@ -403,8 +527,9 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
|
|
403
527
|
|
404
528
|
Args:
|
405
529
|
runs: The list of runs to filter.
|
406
|
-
config: The configuration object to filter the runs. This can be any
|
407
|
-
provides key-value pairs through the `iter_params`
|
530
|
+
config: The configuration object to filter the runs. This can be any
|
531
|
+
object that provides key-value pairs through the `iter_params`
|
532
|
+
function.
|
408
533
|
**kwargs: Additional key-value pairs to filter the runs.
|
409
534
|
|
410
535
|
Returns:
|
@@ -419,7 +544,38 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
|
|
419
544
|
return runs
|
420
545
|
|
421
546
|
|
422
|
-
def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run
|
547
|
+
def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
|
548
|
+
"""
|
549
|
+
Find the first run based on the provided configuration.
|
550
|
+
|
551
|
+
This method filters the runs in the collection according to the
|
552
|
+
specified configuration object and returns the first run that matches
|
553
|
+
the provided parameters. If no run matches the criteria, a `ValueError` is
|
554
|
+
raised.
|
555
|
+
|
556
|
+
Args:
|
557
|
+
runs: The runs to filter.
|
558
|
+
config: The configuration object to identify the run.
|
559
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
560
|
+
|
561
|
+
Returns:
|
562
|
+
The first run object that matches the provided configuration.
|
563
|
+
|
564
|
+
Raises:
|
565
|
+
ValueError: If no run matches the criteria.
|
566
|
+
|
567
|
+
See Also:
|
568
|
+
RunCollection.filter: The method that performs the actual filtering logic.
|
569
|
+
"""
|
570
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
571
|
+
|
572
|
+
if len(filtered_runs) == 0:
|
573
|
+
raise ValueError("No run matches the provided configuration.")
|
574
|
+
|
575
|
+
return filtered_runs[0]
|
576
|
+
|
577
|
+
|
578
|
+
def try_find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
|
423
579
|
"""
|
424
580
|
Find the first run based on the provided configuration.
|
425
581
|
|
@@ -436,11 +592,47 @@ def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | N
|
|
436
592
|
The first run object that matches the provided configuration, or None
|
437
593
|
if no runs match the criteria.
|
438
594
|
"""
|
439
|
-
|
440
|
-
|
595
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
596
|
+
|
597
|
+
if len(filtered_runs) == 0:
|
598
|
+
return None
|
599
|
+
|
600
|
+
return filtered_runs[0]
|
441
601
|
|
442
602
|
|
443
|
-
def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run
|
603
|
+
def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
|
604
|
+
"""
|
605
|
+
Find the last run based on the provided configuration.
|
606
|
+
|
607
|
+
This method filters the runs in the collection according to the
|
608
|
+
specified configuration object and returns the last run that matches
|
609
|
+
the provided parameters. If no run matches the criteria, a `ValueError`
|
610
|
+
is raised.
|
611
|
+
|
612
|
+
Args:
|
613
|
+
runs: The runs to filter.
|
614
|
+
config: The configuration object to identify the run.
|
615
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
616
|
+
|
617
|
+
Returns:
|
618
|
+
The last run object that matches the provided configuration.
|
619
|
+
|
620
|
+
Raises:
|
621
|
+
ValueError: If no run matches the criteria.
|
622
|
+
|
623
|
+
See Also:
|
624
|
+
RunCollection.filter: The method that performs the actual filtering
|
625
|
+
logic.
|
626
|
+
"""
|
627
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
628
|
+
|
629
|
+
if len(filtered_runs) == 0:
|
630
|
+
raise ValueError("No run matches the provided configuration.")
|
631
|
+
|
632
|
+
return filtered_runs[-1]
|
633
|
+
|
634
|
+
|
635
|
+
def try_find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
|
444
636
|
"""
|
445
637
|
Find the last run based on the provided configuration.
|
446
638
|
|
@@ -457,18 +649,62 @@ def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Ru
|
|
457
649
|
The last run object that matches the provided configuration, or None
|
458
650
|
if no runs match the criteria.
|
459
651
|
"""
|
460
|
-
|
461
|
-
|
652
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
653
|
+
|
654
|
+
if len(filtered_runs) == 0:
|
655
|
+
return None
|
656
|
+
|
657
|
+
return filtered_runs[-1]
|
658
|
+
|
659
|
+
|
660
|
+
def get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
|
661
|
+
"""
|
662
|
+
Retrieve a specific run based on the provided configuration.
|
663
|
+
|
664
|
+
This method filters the runs in the collection according to the
|
665
|
+
specified configuration object and returns the run that matches
|
666
|
+
the provided parameters. If no run matches the criteria, or if more
|
667
|
+
than one run matches the criteria, a `ValueError` is raised.
|
668
|
+
|
669
|
+
Args:
|
670
|
+
runs: The runs to filter.
|
671
|
+
config: The configuration object to identify the run.
|
672
|
+
**kwargs: Additional key-value pairs to filter the runs.
|
673
|
+
|
674
|
+
Returns:
|
675
|
+
The run object that matches the provided configuration.
|
676
|
+
|
677
|
+
Raises:
|
678
|
+
ValueError: If no run matches the criteria or if more than one run
|
679
|
+
matches the criteria.
|
680
|
+
|
681
|
+
See Also:
|
682
|
+
RunCollection.filter: The method that performs the actual filtering
|
683
|
+
logic.
|
684
|
+
"""
|
685
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
462
686
|
|
687
|
+
if len(filtered_runs) == 0:
|
688
|
+
raise ValueError("No run matches the provided configuration.")
|
463
689
|
|
464
|
-
|
690
|
+
if len(filtered_runs) == 1:
|
691
|
+
return filtered_runs[0]
|
692
|
+
|
693
|
+
msg = (
|
694
|
+
f"Multiple runs were filtered. Expected number of runs is 1, "
|
695
|
+
f"but found {len(filtered_runs)} runs."
|
696
|
+
)
|
697
|
+
raise ValueError(msg)
|
698
|
+
|
699
|
+
|
700
|
+
def try_get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
|
465
701
|
"""
|
466
702
|
Retrieve a specific run based on the provided configuration.
|
467
703
|
|
468
704
|
This method filters the runs in the collection according to the
|
469
705
|
specified configuration object and returns the run that matches
|
470
|
-
the provided parameters. If
|
471
|
-
a `ValueError` is raised.
|
706
|
+
the provided parameters. If no run matches the criteria, None is returned.
|
707
|
+
If more than one run matches the criteria, a `ValueError` is raised.
|
472
708
|
|
473
709
|
Args:
|
474
710
|
runs: The runs to filter.
|
@@ -481,16 +717,23 @@ def get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | No
|
|
481
717
|
|
482
718
|
Raises:
|
483
719
|
ValueError: If more than one run matches the criteria.
|
720
|
+
|
721
|
+
See Also:
|
722
|
+
RunCollection.filter: The method that performs the actual filtering
|
723
|
+
logic.
|
484
724
|
"""
|
485
|
-
|
725
|
+
filtered_runs = filter_runs(runs, config, **kwargs)
|
486
726
|
|
487
|
-
if len(
|
727
|
+
if len(filtered_runs) == 0:
|
488
728
|
return None
|
489
729
|
|
490
|
-
if len(
|
491
|
-
return
|
730
|
+
if len(filtered_runs) == 1:
|
731
|
+
return filtered_runs[0]
|
492
732
|
|
493
|
-
msg =
|
733
|
+
msg = (
|
734
|
+
"Multiple runs were filtered. Expected number of runs is 1, "
|
735
|
+
f"but found {len(filtered_runs)} runs."
|
736
|
+
)
|
494
737
|
raise ValueError(msg)
|
495
738
|
|
496
739
|
|
@@ -498,9 +741,9 @@ def get_param_names(runs: list[Run]) -> list[str]:
|
|
498
741
|
"""
|
499
742
|
Get the parameter names from the runs.
|
500
743
|
|
501
|
-
This method extracts the unique parameter names from the provided list of
|
502
|
-
It iterates through each run and collects the parameter names into a
|
503
|
-
ensure uniqueness.
|
744
|
+
This method extracts the unique parameter names from the provided list of
|
745
|
+
runs. It iterates through each run and collects the parameter names into a
|
746
|
+
set to ensure uniqueness.
|
504
747
|
|
505
748
|
Args:
|
506
749
|
runs: The list of runs from which to extract parameter names.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: hydraflow
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.3
|
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
|
@@ -20,7 +20,9 @@ Requires-Dist: hydra-core>1.3
|
|
20
20
|
Requires-Dist: mlflow>2.15
|
21
21
|
Requires-Dist: setuptools
|
22
22
|
Requires-Dist: watchdog
|
23
|
+
Requires-Dist: watchfiles
|
23
24
|
Provides-Extra: dev
|
25
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
24
26
|
Requires-Dist: pytest-clarity; extra == 'dev'
|
25
27
|
Requires-Dist: pytest-cov; extra == 'dev'
|
26
28
|
Requires-Dist: pytest-randomly; extra == 'dev'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
hydraflow/__init__.py,sha256=9v7p2ezUd_LMoRJQS0ay8c7fpaqPZ6Ofq7YPT0rSO5I,528
|
2
|
+
hydraflow/asyncio.py,sha256=yh851L315QHzRBwq6r-uwO2oZKgz1JawHp-fswfxT1E,6175
|
3
|
+
hydraflow/config.py,sha256=FNTuCppjCMrZKVByJMrWKbgj3HeMWWwAmQNoyFe029Y,2087
|
4
|
+
hydraflow/context.py,sha256=MqkEhKEZL_N3eb3v5u9D4EqKkiSmiPyXXafhPkALRlg,5129
|
5
|
+
hydraflow/mlflow.py,sha256=_Los9E38eG8sTiN8bGwZmvjCrS0S-wSGiA4fyhQM3Zw,2251
|
6
|
+
hydraflow/runs.py,sha256=0BXSBbNkELP3CzaCGBkejOkpyk5uQUxrdknJPRwR400,29022
|
7
|
+
hydraflow-0.2.3.dist-info/METADATA,sha256=h5Pxy6EnxTlyyGL8NRr14ZHtLhA9ldmM9GP5sES6KWU,4304
|
8
|
+
hydraflow-0.2.3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
9
|
+
hydraflow-0.2.3.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
10
|
+
hydraflow-0.2.3.dist-info/RECORD,,
|
hydraflow-0.2.2.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
hydraflow/__init__.py,sha256=ht4I3q_Ronw2jzk_QRsV-IzObR31F_4Wy7Ve0syNm-8,496
|
2
|
-
hydraflow/config.py,sha256=FNTuCppjCMrZKVByJMrWKbgj3HeMWWwAmQNoyFe029Y,2087
|
3
|
-
hydraflow/context.py,sha256=MqkEhKEZL_N3eb3v5u9D4EqKkiSmiPyXXafhPkALRlg,5129
|
4
|
-
hydraflow/mlflow.py,sha256=_Los9E38eG8sTiN8bGwZmvjCrS0S-wSGiA4fyhQM3Zw,2251
|
5
|
-
hydraflow/runs.py,sha256=kO7Gl9CeS2HjB0y_emGXNMRJTxNoqXBEJ7Ggq96nhMg,22050
|
6
|
-
hydraflow-0.2.2.dist-info/METADATA,sha256=C2lfD6jTDdHyexxATZWfdRQHAUgSOHx7IgvmBUj4tTQ,4232
|
7
|
-
hydraflow-0.2.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
8
|
-
hydraflow-0.2.2.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
|
9
|
-
hydraflow-0.2.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|