hydraflow 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
hydraflow/runs.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
- This module provides functionality for managing and interacting with MLflow runs.
3
- It includes the `RunCollection` class and various methods to filter runs,
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,49 @@ 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 results
41
- as a `RunCollection` object. It allows for flexible searching of MLflow runs based on
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 IDs or
49
- experiment names, but not both in the same call. Values other than
50
- ``None`` or ``[]`` will result in error if ``experiment_names`` is
51
- also not ``None`` or ``[]``. ``None`` will default to the active
52
- experiment if ``experiment_names`` is ``None`` or ``[]``.
53
- filter_string: Filter query string, defaults to searching all runs.
54
- run_view_type: one of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``, or ``ALL`` runs
55
- defined in :py:class:`mlflow.entities.ViewType`.
56
- max_results: The maximum number of runs to put in the dataframe. Default is 100,000
57
- to avoid causing out-of-memory issues on the user's machine.
58
- order_by: List of columns to order by (e.g., "metrics.rmse"). The ``order_by`` column
59
- can contain an optional ``DESC`` or ``ASC`` value. The default is ``ASC``.
60
- The default ordering is to sort by ``start_time DESC``, then ``run_id``.
61
- search_all_experiments: Boolean specifying whether all experiments should be searched.
62
- Only honored if ``experiment_ids`` is ``[]`` or ``None``.
63
- experiment_names: List of experiment names. Search can work with experiment IDs or
64
- experiment names, but not both in the same call. Values other
65
- than ``None`` or ``[]`` will result in error if ``experiment_ids``
66
- is also not ``None`` or ``[]``. ``None`` will default to the active
67
- experiment if ``experiment_ids`` is ``None`` or ``[]``.
48
+ experiment_ids (list[str] | None): List of experiment IDs. Search can
49
+ work with experiment IDs or experiment names, but not both in the
50
+ same call. Values other than ``None`` or ``[]`` will result in
51
+ error if ``experiment_names`` is also not ``None`` or ``[]``.
52
+ ``None`` will default to the active experiment if ``experiment_names``
53
+ is ``None`` or ``[]``.
54
+ experiment_ids (list[str] | None): List of experiment IDs. Search can
55
+ work with experiment IDs or experiment names, but not both in the
56
+ same call. Values other than ``None`` or ``[]`` will result in
57
+ error if ``experiment_names`` is also not ``None`` or ``[]``.
58
+ ``experiment_names`` is also not ``None`` or ``[]``. ``None`` will
59
+ default to the active experiment if ``experiment_names`` is ``None``
60
+ or ``[]``.
61
+ filter_string (str): Filter query string, defaults to searching all
62
+ runs.
63
+ run_view_type (int): one of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``,
64
+ or ``ALL`` runs defined in :py:class:`mlflow.entities.ViewType`.
65
+ max_results (int): The maximum number of runs to put in the dataframe.
66
+ Default is 100,000 to avoid causing out-of-memory issues on the user's
67
+ machine.
68
+ order_by (list[str] | None): List of columns to order by (e.g.,
69
+ "metrics.rmse"). The ``order_by`` column can contain an optional
70
+ ``DESC`` or ``ASC`` value. The default is ``ASC``. The default
71
+ ordering is to sort by ``start_time DESC``, then ``run_id``.
72
+ ``start_time DESC``, then ``run_id``.
73
+ search_all_experiments (bool): Boolean specifying whether all
74
+ experiments should be searched. Only honored if ``experiment_ids``
75
+ is ``[]`` or ``None``.
76
+ experiment_names (list[str] | None): List of experiment names. Search
77
+ can work with experiment IDs or experiment names, but not both in
78
+ the same call. Values other than ``None`` or ``[]`` will result in
79
+ error if ``experiment_ids`` is also not ``None`` or ``[]``.
80
+ ``experiment_ids`` is also not ``None`` or ``[]``. ``None`` will
81
+ default to the active experiment if ``experiment_ids`` is ``None``
82
+ or ``[]``.
68
83
 
69
84
  Returns:
70
85
  A `RunCollection` object containing the search results.
@@ -97,10 +112,10 @@ def list_runs(experiment_names: list[str] | None = None) -> RunCollection:
97
112
  The returned runs are sorted by their start time in ascending order.
98
113
 
99
114
  Args:
100
- experiment_names: List of experiment names to search for runs.
101
- If None or an empty list is provided, the function will search
102
- the currently active experiment or all experiments except the
103
- "Default" experiment.
115
+ experiment_names (list[str] | None): List of experiment names to search
116
+ for runs. If None or an empty list is provided, the function will
117
+ search the currently active experiment or all experiments except
118
+ the "Default" experiment.
104
119
 
105
120
  Returns:
106
121
  A `RunCollection` object containing the runs for the specified experiments.
@@ -133,27 +148,93 @@ class RunCollection:
133
148
  def __len__(self) -> int:
134
149
  return len(self._runs)
135
150
 
151
+ def __iter__(self) -> Iterator[Run]:
152
+ return iter(self._runs)
153
+
154
+ def __getitem__(self, index: int) -> Run:
155
+ return self._runs[index]
156
+
157
+ def __contains__(self, run: Run) -> bool:
158
+ return run in self._runs
159
+
160
+ def sort(
161
+ self,
162
+ key: Callable[[Run], Any] | None = None,
163
+ reverse: bool = False,
164
+ ) -> None:
165
+ self._runs.sort(key=key or (lambda x: x.info.start_time), reverse=reverse)
166
+
167
+ def first(self) -> Run:
168
+ """
169
+ Get the first run in the collection.
170
+
171
+ Returns:
172
+ The first run object in the collection.
173
+
174
+ Raises:
175
+ ValueError: If the collection is empty.
176
+ """
177
+ if not self._runs:
178
+ raise ValueError("The collection is empty.")
179
+
180
+ return self._runs[0]
181
+
182
+ def try_first(self) -> Run | None:
183
+ """
184
+ Try to get the first run in the collection.
185
+
186
+ Returns:
187
+ The first run object in the collection, or None if the collection
188
+ is empty.
189
+ """
190
+ return self._runs[0] if self._runs else None
191
+
192
+ def last(self) -> Run:
193
+ """
194
+ Get the last run in the collection.
195
+
196
+ Returns:
197
+ The last run object in the collection.
198
+
199
+ Raises:
200
+ ValueError: If the collection is empty.
201
+ """
202
+ if not self._runs:
203
+ raise ValueError("The collection is empty.")
204
+
205
+ return self._runs[-1]
206
+
207
+ def try_last(self) -> Run | None:
208
+ """
209
+ Try to get the last run in the collection.
210
+
211
+ Returns:
212
+ The last run object in the collection, or None if the collection is
213
+ empty.
214
+ """
215
+ return self._runs[-1] if self._runs else None
216
+
136
217
  def filter(self, config: object | None = None, **kwargs) -> RunCollection:
137
218
  """
138
219
  Filter the runs based on the provided configuration.
139
220
 
140
221
  This method filters the runs in the collection according to the
141
- specified configuration object and additional key-value pairs.
142
- The configuration object and key-value pairs should contain
143
- key-value pairs that correspond to the parameters of the runs.
144
- Only the runs that match all the specified parameters will be
145
- included in the returned `RunCollection` object.
222
+ specified configuration object and additional key-value pairs. The
223
+ configuration object and key-value pairs should contain key-value pairs
224
+ that correspond to the parameters of the runs. Only the runs that match
225
+ all the specified parameters will be included in the returned
226
+ `RunCollection` object.
146
227
 
147
228
  The filtering supports:
148
229
  - Exact matches for single values.
149
230
  - Membership checks for lists of values.
150
- - Range checks for tuples of two values (inclusive of the lower bound and
151
- exclusive of the upper bound).
231
+ - Range checks for tuples of two values (inclusive of the lower bound
232
+ and exclusive of the upper bound).
152
233
 
153
234
  Args:
154
- config: The configuration object to filter the runs. This can be any
155
- object that provides key-value pairs through the `iter_params`
156
- function.
235
+ config (object | None): The configuration object to filter the runs.
236
+ This can be any object that provides key-value pairs through
237
+ the `iter_params` function.
157
238
  **kwargs: Additional key-value pairs to filter the runs.
158
239
 
159
240
  Returns:
@@ -161,80 +242,161 @@ class RunCollection:
161
242
  """
162
243
  return RunCollection(filter_runs(self._runs, config, **kwargs))
163
244
 
164
- def find(self, config: object | None = None, **kwargs) -> Run | None:
245
+ def find(self, config: object | None = None, **kwargs) -> Run:
165
246
  """
166
247
  Find the first run based on the provided configuration.
167
248
 
168
249
  This method filters the runs in the collection according to the
169
250
  specified configuration object and returns the first run that matches
170
- the provided parameters. If no run matches the criteria, None is returned.
251
+ the provided parameters. If no run matches the criteria, a `ValueError`
252
+ is raised.
171
253
 
172
254
  Args:
173
- config: The configuration object to identify the run.
255
+ config (object | None): The configuration object to identify the run.
174
256
  **kwargs: Additional key-value pairs to filter the runs.
175
257
 
176
258
  Returns:
177
- The first run object that matches the provided configuration, or None
178
- if no runs match the criteria.
259
+ The first run object that matches the provided configuration.
260
+
261
+ Raises:
262
+ ValueError: If no run matches the criteria.
179
263
 
180
264
  See Also:
181
- RunCollection.filter: The method that performs the actual filtering logic.
265
+ RunCollection.filter: The method that performs the actual filtering
266
+ logic.
182
267
  """
183
268
  return find_run(self._runs, config, **kwargs)
184
269
 
185
- def find_last(self, config: object | None = None, **kwargs) -> Run | None:
270
+ def try_find(self, config: object | None = None, **kwargs) -> Run | None:
271
+ """
272
+ Find the first run based on the provided configuration.
273
+
274
+ This method filters the runs in the collection according to the
275
+ specified configuration object and returns the first run that matches
276
+ the provided parameters. If no run matches the criteria, None is
277
+ returned.
278
+
279
+ Args:
280
+ config (object | None): The configuration object to identify the run.
281
+ **kwargs: Additional key-value pairs to filter the runs.
282
+
283
+ Returns:
284
+ The first run object that matches the provided configuration, or
285
+ None if no runs match the criteria.
286
+
287
+ See Also:
288
+ RunCollection.filter: The method that performs the actual filtering
289
+ logic.
290
+ """
291
+ return try_find_run(self._runs, config, **kwargs)
292
+
293
+ def find_last(self, config: object | None = None, **kwargs) -> Run:
186
294
  """
187
295
  Find the last run based on the provided configuration.
188
296
 
189
297
  This method filters the runs in the collection according to the
190
298
  specified configuration object and returns the last run that matches
191
- the provided parameters. If no run matches the criteria, None is returned.
299
+ the provided parameters. If no run matches the criteria, a `ValueError`
300
+ is raised.
192
301
 
193
302
  Args:
194
- config: The configuration object to identify the run.
303
+ config (object | None): The configuration object to identify the run.
195
304
  **kwargs: Additional key-value pairs to filter the runs.
196
305
 
197
306
  Returns:
198
- The last run object that matches the provided configuration, or None
199
- if no runs match the criteria.
307
+ The last run object that matches the provided configuration.
308
+
309
+ Raises:
310
+ ValueError: If no run matches the criteria.
200
311
 
201
312
  See Also:
202
- RunCollection.filter: The method that performs the actual filtering logic.
313
+ RunCollection.filter: The method that performs the actual filtering
314
+ logic.
203
315
  """
204
316
  return find_last_run(self._runs, config, **kwargs)
205
317
 
206
- def get(self, config: object | None = None, **kwargs) -> Run | None:
318
+ def try_find_last(self, config: object | None = None, **kwargs) -> Run | None:
319
+ """
320
+ Find the last run based on the provided configuration.
321
+
322
+ This method filters the runs in the collection according to the
323
+ specified configuration object and returns the last run that matches
324
+ the provided parameters. If no run matches the criteria, None is
325
+ returned.
326
+
327
+ Args:
328
+ config (object | None): The configuration object to identify the run.
329
+ **kwargs: Additional key-value pairs to filter the runs.
330
+
331
+ Returns:
332
+ The last run object that matches the provided configuration, or
333
+ None if no runs match the criteria.
334
+
335
+ See Also:
336
+ RunCollection.filter: The method that performs the actual filtering
337
+ logic.
338
+ """
339
+ return try_find_last_run(self._runs, config, **kwargs)
340
+
341
+ def get(self, config: object | None = None, **kwargs) -> Run:
207
342
  """
208
343
  Retrieve a specific run based on the provided configuration.
209
344
 
210
345
  This method filters the runs in the collection according to the
211
- specified configuration object and returns the run that matches
212
- the provided parameters. If more than one run matches the criteria,
213
- a `ValueError` is raised.
346
+ specified configuration object and returns the run that matches the
347
+ provided parameters. If no run matches the criteria, or if more than
348
+ one run matches the criteria, a `ValueError` is raised.
214
349
 
215
350
  Args:
216
- config: The configuration object to identify the run.
351
+ config (object | None): The configuration object to identify the run.
217
352
  **kwargs: Additional key-value pairs to filter the runs.
218
353
 
219
354
  Returns:
220
- The run object that matches the provided configuration, or None
221
- if no runs match the criteria.
355
+ The run object that matches the provided configuration.
222
356
 
223
357
  Raises:
224
- ValueError: If more than one run matches the criteria.
358
+ ValueError: If no run matches the criteria or if more than one run
359
+ matches the criteria.
225
360
 
226
361
  See Also:
227
- RunCollection.filter: The method that performs the actual filtering logic.
362
+ RunCollection.filter: The method that performs the actual filtering
363
+ logic.
228
364
  """
229
365
  return get_run(self._runs, config, **kwargs)
230
366
 
367
+ def try_get(self, config: object | None = None, **kwargs) -> Run | None:
368
+ """
369
+ Retrieve a specific run based on the provided configuration.
370
+
371
+ This method filters the runs in the collection according to the
372
+ specified configuration object and returns the run that matches the
373
+ provided parameters. If no run matches the criteria, None is returned.
374
+ If more than one run matches the criteria, a `ValueError` is raised.
375
+
376
+ Args:
377
+ config (object | None): The configuration object to identify the run.
378
+ **kwargs: Additional key-value pairs to filter the runs.
379
+
380
+ Returns:
381
+ The run object that matches the provided configuration, or None if
382
+ no runs match the criteria.
383
+
384
+ Raises:
385
+ ValueError: If more than one run matches the criteria.
386
+
387
+ See Also:
388
+ RunCollection.filter: The method that performs the actual filtering
389
+ logic.
390
+ """
391
+ return try_get_run(self._runs, config, **kwargs)
392
+
231
393
  def get_param_names(self) -> list[str]:
232
394
  """
233
395
  Get the parameter names from the runs.
234
396
 
235
- This method extracts the unique parameter names from the provided list of runs.
236
- It iterates through each run and collects the parameter names into a set to
237
- ensure uniqueness.
397
+ This method extracts the unique parameter names from the provided list
398
+ of runs. It iterates through each run and collects the parameter names
399
+ into a set to ensure uniqueness.
238
400
 
239
401
  Returns:
240
402
  A list of unique parameter names.
@@ -246,101 +408,95 @@ class RunCollection:
246
408
  Get the parameter dictionary from the list of runs.
247
409
 
248
410
  This method extracts the parameter names and their corresponding values
249
- from the provided list of runs. It iterates through each run and collects
250
- the parameter values into a dictionary where the keys are parameter names
251
- and the values are lists of parameter values.
411
+ from the provided list of runs. It iterates through each run and
412
+ collects the parameter values into a dictionary where the keys are
413
+ parameter names and the values are lists of parameter values.
252
414
 
253
415
  Returns:
254
- A dictionary where the keys are parameter names and the values are lists
255
- of parameter values.
416
+ A dictionary where the keys are parameter names and the values are
417
+ lists of parameter values.
256
418
  """
257
419
  return get_param_dict(self._runs)
258
420
 
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
421
  def map(self, func: Callable[[Run], T]) -> Iterator[T]:
278
422
  """
279
- Apply a function to each run in the collection and return an iterator of results.
423
+ Apply a function to each run in the collection and return an iterator of
424
+ results.
280
425
 
281
426
  Args:
282
- func: A function that takes a Run object and returns a result.
427
+ func (Callable[[Run], T]): A function that takes a run and returns a
428
+ result.
283
429
 
284
- Returns:
285
- An iterator of results obtained by applying the function to each run
286
- in the collection.
430
+ Yields:
431
+ Results obtained by applying the function to each run in the
432
+ collection.
287
433
  """
288
434
  return (func(run) for run in self._runs)
289
435
 
290
436
  def map_run_id(self, func: Callable[[str], T]) -> Iterator[T]:
291
437
  """
292
- Apply a function to each run id in the collection and return an iterator of results.
438
+ Apply a function to each run id in the collection and return an iterator
439
+ of results.
293
440
 
294
441
  Args:
295
- func: A function that takes a run id and returns a result.
442
+ func (Callable[[str], T]): A function that takes a run id and returns a
443
+ result.
296
444
 
297
- Returns:
298
- An iterator of results obtained by applying the function to each run id
299
- in the collection.
445
+ Yields:
446
+ Results obtained by applying the function to each run id in the
447
+ collection.
300
448
  """
301
449
  return (func(run.info.run_id) for run in self._runs)
302
450
 
303
451
  def map_config(self, func: Callable[[DictConfig], T]) -> Iterator[T]:
304
452
  """
305
- Apply a function to each run config in the collection and return an iterator of results.
453
+ Apply a function to each run configuration in the collection and return
454
+ an iterator of results.
306
455
 
307
456
  Args:
308
- func: A function that takes a run config and returns a result.
457
+ func (Callable[[DictConfig], T]): A function that takes a run
458
+ configuration and returns a result.
309
459
 
310
- Returns:
311
- An iterator of results obtained by applying the function to each run config
460
+ Yields:
461
+ Results obtained by applying the function to each run configuration
312
462
  in the collection.
313
463
  """
314
464
  return (func(load_config(run)) for run in self._runs)
315
465
 
316
466
  def map_uri(self, func: Callable[[str | None], T]) -> Iterator[T]:
317
467
  """
318
- Apply a function to each artifact URI in the collection and return an iterator of results.
468
+ Apply a function to each artifact URI in the collection and return an
469
+ iterator of results.
319
470
 
320
- This method iterates over each run in the collection, retrieves the artifact URI,
321
- and applies the provided function to it. If a run does not have an artifact URI,
322
- None is passed to the function.
471
+ This method iterates over each run in the collection, retrieves the
472
+ artifact URI, and applies the provided function to it. If a run does not
473
+ have an artifact URI, None is passed to the function.
323
474
 
324
475
  Args:
325
- func: A function that takes an artifact URI (string or None) and returns a result.
476
+ func (Callable[[str | None], T]): A function that takes an
477
+ artifact URI (string or None) and returns a result.
326
478
 
327
479
  Yields:
328
- The results obtained by applying the function to each artifact URI in the collection.
480
+ Results obtained by applying the function to each artifact URI in the
481
+ collection.
329
482
  """
330
483
  return (func(run.info.artifact_uri) for run in self._runs)
331
484
 
332
485
  def map_dir(self, func: Callable[[str], T]) -> Iterator[T]:
333
486
  """
334
- Apply a function to each artifact directory in the collection and return an iterator of results.
487
+ Apply a function to each artifact directory in the collection and return
488
+ an iterator of results.
335
489
 
336
- This method iterates over each run in the collection, downloads the artifact directory,
337
- and applies the provided function to the directory path.
490
+ This method iterates over each run in the collection, downloads the
491
+ artifact directory, and applies the provided function to the directory
492
+ path.
338
493
 
339
494
  Args:
340
- func: A function that takes an artifact directory path (string) and returns a result.
495
+ func (Callable[[str], T]): A function that takes an artifact directory
496
+ path (string) and returns a result.
341
497
 
342
- Returns:
343
- An iterator of results obtained by applying the function to each artifact directory
498
+ Yields:
499
+ Results obtained by applying the function to each artifact directory
344
500
  in the collection.
345
501
  """
346
502
  return (func(download_artifacts(run_id=run.info.run_id)) for run in self._runs)
@@ -350,25 +506,21 @@ def _param_matches(run: Run, key: str, value: Any) -> bool:
350
506
  """
351
507
  Check if the run's parameter matches the specified key-value pair.
352
508
 
353
- This function checks if the run's parameters contain the specified key-value pair.
354
- It handles different types of values, including lists and tuples.
509
+ This function checks if the run's parameters contain the specified
510
+ key-value pair. It handles different types of values, including lists
511
+ and tuples.
355
512
 
356
513
  Args:
357
- run: The run object to check.
358
- key: The parameter key to check.
359
- value: The parameter value to check.
514
+ run (Run): The run object to check.
515
+ key (str): The parameter key to check.
516
+ value (Any): The parameter value to check.
360
517
 
361
518
  Returns:
362
- True if the run's parameter matches the specified key-value pair, False otherwise.
519
+ True if the run's parameter matches the specified key-value pair,
520
+ False otherwise.
363
521
  """
364
522
  param = run.data.params.get(key, value)
365
523
 
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
524
  if param is None:
373
525
  return False
374
526
 
@@ -392,8 +544,8 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
392
544
  specified configuration object and additional key-value pairs.
393
545
  The configuration object and key-value pairs should contain
394
546
  key-value pairs that correspond to the parameters of the runs.
395
- Only the runs that match all the specified parameters will be
396
- included in the returned list of runs.
547
+ Only the runs that match all the specified parameters will
548
+ be included in the returned list of runs.
397
549
 
398
550
  The filtering supports:
399
551
  - Exact matches for single values.
@@ -402,9 +554,10 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
402
554
  exclusive of the upper bound).
403
555
 
404
556
  Args:
405
- runs: The list of runs to filter.
406
- config: The configuration object to filter the runs. This can be any object that
407
- provides key-value pairs through the `iter_params` function.
557
+ runs (list[Run]): The list of runs to filter.
558
+ config (object | None): The configuration object to filter the runs.
559
+ This can be any object that provides key-value pairs through the
560
+ `iter_params` function.
408
561
  **kwargs: Additional key-value pairs to filter the runs.
409
562
 
410
563
  Returns:
@@ -419,7 +572,38 @@ def filter_runs(runs: list[Run], config: object | None = None, **kwargs) -> list
419
572
  return runs
420
573
 
421
574
 
422
- def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
575
+ def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
576
+ """
577
+ Find the first run based on the provided configuration.
578
+
579
+ This method filters the runs in the collection according to the
580
+ specified configuration object and returns the first run that matches
581
+ the provided parameters. If no run matches the criteria, a `ValueError` is
582
+ raised.
583
+
584
+ Args:
585
+ runs (list[Run]): The runs to filter.
586
+ config (object | None): The configuration object to identify the run.
587
+ **kwargs: Additional key-value pairs to filter the runs.
588
+
589
+ Returns:
590
+ The first run object that matches the provided configuration.
591
+
592
+ Raises:
593
+ ValueError: If no run matches the criteria.
594
+
595
+ See Also:
596
+ RunCollection.filter: The method that performs the actual filtering logic.
597
+ """
598
+ filtered_runs = filter_runs(runs, config, **kwargs)
599
+
600
+ if len(filtered_runs) == 0:
601
+ raise ValueError("No run matches the provided configuration.")
602
+
603
+ return filtered_runs[0]
604
+
605
+
606
+ def try_find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
423
607
  """
424
608
  Find the first run based on the provided configuration.
425
609
 
@@ -428,19 +612,55 @@ def find_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | N
428
612
  the provided parameters. If no run matches the criteria, None is returned.
429
613
 
430
614
  Args:
431
- runs: The runs to filter.
432
- config: The configuration object to identify the run.
615
+ runs (list[Run]): The runs to filter.
616
+ config (object | None): The configuration object to identify the run.
433
617
  **kwargs: Additional key-value pairs to filter the runs.
434
618
 
435
619
  Returns:
436
620
  The first run object that matches the provided configuration, or None
437
621
  if no runs match the criteria.
438
622
  """
439
- runs = filter_runs(runs, config, **kwargs)
440
- return runs[0] if runs else None
623
+ filtered_runs = filter_runs(runs, config, **kwargs)
624
+
625
+ if len(filtered_runs) == 0:
626
+ return None
627
+
628
+ return filtered_runs[0]
629
+
630
+
631
+ def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
632
+ """
633
+ Find the last run based on the provided configuration.
634
+
635
+ This method filters the runs in the collection according to the
636
+ specified configuration object and returns the last run that matches
637
+ the provided parameters. If no run matches the criteria, a `ValueError`
638
+ is raised.
639
+
640
+ Args:
641
+ runs (list[Run]): The runs to filter.
642
+ config (object | None): The configuration object to identify the run.
643
+ **kwargs: Additional key-value pairs to filter the runs.
644
+
645
+ Returns:
646
+ The last run object that matches the provided configuration.
647
+
648
+ Raises:
649
+ ValueError: If no run matches the criteria.
650
+
651
+ See Also:
652
+ RunCollection.filter: The method that performs the actual filtering
653
+ logic.
654
+ """
655
+ filtered_runs = filter_runs(runs, config, **kwargs)
441
656
 
657
+ if len(filtered_runs) == 0:
658
+ raise ValueError("No run matches the provided configuration.")
442
659
 
443
- def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
660
+ return filtered_runs[-1]
661
+
662
+
663
+ def try_find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
444
664
  """
445
665
  Find the last run based on the provided configuration.
446
666
 
@@ -449,30 +669,74 @@ def find_last_run(runs: list[Run], config: object | None = None, **kwargs) -> Ru
449
669
  the provided parameters. If no run matches the criteria, None is returned.
450
670
 
451
671
  Args:
452
- runs: The runs to filter.
453
- config: The configuration object to identify the run.
672
+ runs (list[Run]): The runs to filter.
673
+ config (object | None): The configuration object to identify the run.
454
674
  **kwargs: Additional key-value pairs to filter the runs.
455
675
 
456
676
  Returns:
457
677
  The last run object that matches the provided configuration, or None
458
678
  if no runs match the criteria.
459
679
  """
460
- runs = filter_runs(runs, config, **kwargs)
461
- return runs[-1] if runs else None
680
+ filtered_runs = filter_runs(runs, config, **kwargs)
681
+
682
+ if len(filtered_runs) == 0:
683
+ return None
684
+
685
+ return filtered_runs[-1]
462
686
 
463
687
 
464
- def get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
688
+ def get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run:
465
689
  """
466
690
  Retrieve a specific run based on the provided configuration.
467
691
 
468
692
  This method filters the runs in the collection according to the
469
693
  specified configuration object and returns the run that matches
470
- the provided parameters. If more than one run matches the criteria,
471
- a `ValueError` is raised.
694
+ the provided parameters. If no run matches the criteria, or if more
695
+ than one run matches the criteria, a `ValueError` is raised.
472
696
 
473
697
  Args:
474
- runs: The runs to filter.
475
- config: The configuration object to identify the run.
698
+ runs (list[Run]): The runs to filter.
699
+ config (object | None): The configuration object to identify the run.
700
+ **kwargs: Additional key-value pairs to filter the runs.
701
+
702
+ Returns:
703
+ The run object that matches the provided configuration.
704
+
705
+ Raises:
706
+ ValueError: If no run matches the criteria or if more than one run
707
+ matches the criteria.
708
+
709
+ See Also:
710
+ RunCollection.filter: The method that performs the actual filtering
711
+ logic.
712
+ """
713
+ filtered_runs = filter_runs(runs, config, **kwargs)
714
+
715
+ if len(filtered_runs) == 0:
716
+ raise ValueError("No run matches the provided configuration.")
717
+
718
+ if len(filtered_runs) == 1:
719
+ return filtered_runs[0]
720
+
721
+ msg = (
722
+ f"Multiple runs were filtered. Expected number of runs is 1, "
723
+ f"but found {len(filtered_runs)} runs."
724
+ )
725
+ raise ValueError(msg)
726
+
727
+
728
+ def try_get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | None:
729
+ """
730
+ Retrieve a specific run based on the provided configuration.
731
+
732
+ This method filters the runs in the collection according to the
733
+ specified configuration object and returns the run that matches
734
+ the provided parameters. If no run matches the criteria, None is returned.
735
+ If more than one run matches the criteria, a `ValueError` is raised.
736
+
737
+ Args:
738
+ runs (list[Run]): The runs to filter.
739
+ config (object | None): The configuration object to identify the run.
476
740
  **kwargs: Additional key-value pairs to filter the runs.
477
741
 
478
742
  Returns:
@@ -481,16 +745,23 @@ def get_run(runs: list[Run], config: object | None = None, **kwargs) -> Run | No
481
745
 
482
746
  Raises:
483
747
  ValueError: If more than one run matches the criteria.
748
+
749
+ See Also:
750
+ RunCollection.filter: The method that performs the actual filtering
751
+ logic.
484
752
  """
485
- runs = filter_runs(runs, config, **kwargs)
753
+ filtered_runs = filter_runs(runs, config, **kwargs)
486
754
 
487
- if len(runs) == 0:
755
+ if len(filtered_runs) == 0:
488
756
  return None
489
757
 
490
- if len(runs) == 1:
491
- return runs[0]
758
+ if len(filtered_runs) == 1:
759
+ return filtered_runs[0]
492
760
 
493
- msg = f"Multiple runs were filtered. Expected number of runs is 1, but found {len(runs)} runs."
761
+ msg = (
762
+ "Multiple runs were filtered. Expected number of runs is 1, "
763
+ f"but found {len(filtered_runs)} runs."
764
+ )
494
765
  raise ValueError(msg)
495
766
 
496
767
 
@@ -498,12 +769,12 @@ def get_param_names(runs: list[Run]) -> list[str]:
498
769
  """
499
770
  Get the parameter names from the runs.
500
771
 
501
- This method extracts the unique parameter names from the provided list of runs.
502
- It iterates through each run and collects the parameter names into a set to
503
- ensure uniqueness.
772
+ This method extracts the unique parameter names from the provided list of
773
+ runs. It iterates through each run and collects the parameter names into a
774
+ set to ensure uniqueness.
504
775
 
505
776
  Args:
506
- runs: The list of runs from which to extract parameter names.
777
+ runs (list[Run]): The list of runs from which to extract parameter names.
507
778
 
508
779
  Returns:
509
780
  A list of unique parameter names.
@@ -527,7 +798,8 @@ def get_param_dict(runs: list[Run]) -> dict[str, list[str]]:
527
798
  and the values are lists of parameter values.
528
799
 
529
800
  Args:
530
- runs: The list of runs from which to extract parameter names and values.
801
+ runs (list[Run]): The list of runs from which to extract parameter names
802
+ and values.
531
803
 
532
804
  Returns:
533
805
  A dictionary where the keys are parameter names and the values are lists
@@ -552,7 +824,7 @@ def load_config(run: Run) -> DictConfig:
552
824
  `.hydra/config.yaml` is not found in the run's artifact directory.
553
825
 
554
826
  Args:
555
- run: The Run instance for which to load the configuration.
827
+ run (Run): The Run instance for which to load the configuration.
556
828
 
557
829
  Returns:
558
830
  The loaded configuration as a DictConfig object. Returns an empty
@@ -570,37 +842,3 @@ def _load_config(run_id: str) -> DictConfig:
570
842
  return DictConfig({})
571
843
 
572
844
  return OmegaConf.load(path) # type: ignore
573
-
574
-
575
- # def get_hydra_output_dir(run: Run_ | Series | str) -> Path:
576
- # """
577
- # Get the Hydra output directory.
578
-
579
- # Args:
580
- # run: The run object.
581
-
582
- # Returns:
583
- # Path: The Hydra output directory.
584
- # """
585
- # path = get_artifact_dir(run) / ".hydra/hydra.yaml"
586
-
587
- # if path.exists():
588
- # hc = OmegaConf.load(path)
589
- # return Path(hc.hydra.runtime.output_dir)
590
-
591
- # raise FileNotFoundError
592
-
593
-
594
- # def log_hydra_output_dir(run: Run_ | Series | str) -> None:
595
- # """
596
- # Log the Hydra output directory.
597
-
598
- # Args:
599
- # run: The run object.
600
-
601
- # Returns:
602
- # None
603
- # """
604
- # output_dir = get_hydra_output_dir(run)
605
- # run_id = run if isinstance(run, str) else run.info.run_id
606
- # mlflow.log_artifacts(output_dir.as_posix(), run_id=run_id)