hydraflow 0.5.4__py3-none-any.whl → 0.6.0__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/config.py CHANGED
@@ -22,6 +22,7 @@ def collect_params(config: object) -> dict[str, Any]:
22
22
 
23
23
  Returns:
24
24
  dict[str, Any]: A dictionary of collected parameters.
25
+
25
26
  """
26
27
  return dict(iter_params(config))
27
28
 
@@ -40,6 +41,7 @@ def iter_params(config: object, prefix: str = "") -> Iterator[tuple[str, Any]]:
40
41
 
41
42
  Yields:
42
43
  Key-value pairs representing the parameters in the configuration object.
44
+
43
45
  """
44
46
  if config is None:
45
47
  return
@@ -113,6 +115,7 @@ def select_config(config: object, names: list[str]) -> dict[str, Any]:
113
115
 
114
116
  Returns:
115
117
  DictConfig: A new configuration object containing only the selected parameters.
118
+
116
119
  """
117
120
  if not isinstance(config, DictConfig):
118
121
  config = OmegaConf.structured(config)
hydraflow/context.py CHANGED
@@ -48,6 +48,7 @@ def log_run(
48
48
  # Perform operations within the MLflow run context
49
49
  pass
50
50
  ```
51
+
51
52
  """
52
53
  if config:
53
54
  log_params(config, synchronous=synchronous)
@@ -55,7 +56,7 @@ def log_run(
55
56
  hc = HydraConfig.get()
56
57
  output_dir = Path(hc.runtime.output_dir)
57
58
 
58
- # Save '.hydra' config directory first.
59
+ # Save '.hydra' config directory.
59
60
  output_subdir = output_dir / (hc.output_subdir or "")
60
61
  mlflow.log_artifacts(output_subdir.as_posix(), hc.output_subdir)
61
62
 
@@ -68,14 +69,41 @@ def log_run(
68
69
  raise
69
70
 
70
71
  finally:
71
- # Save output_dir including '.hydra' config directory.
72
- mlflow.log_artifacts(output_dir.as_posix())
72
+ log_hydra(output_dir)
73
+
74
+
75
+ def log_hydra(output_dir: Path) -> None:
76
+ """Log hydra logs of the current run as artifacts.
77
+
78
+ Args:
79
+ output_dir (Path): The output directory of the Hydra job.
80
+
81
+ """
82
+ uri = mlflow.get_artifact_uri()
83
+ artifact_dir = Path(mlflow.artifacts.download_artifacts(uri))
84
+
85
+ for file_hydra in output_dir.glob("*.log"):
86
+ if not file_hydra.is_file():
87
+ continue
88
+
89
+ file_artifact = artifact_dir / file_hydra.name
90
+ if file_artifact.exists():
91
+ text = file_artifact.read_text()
92
+ if not text.endswith("\n"):
93
+ text += "\n"
94
+ else:
95
+ text = ""
96
+
97
+ text += file_hydra.read_text()
98
+ mlflow.log_text(text, file_hydra.name)
73
99
 
74
100
 
75
101
  @contextmanager
76
102
  def start_run( # noqa: PLR0913
77
103
  config: object,
78
104
  *,
105
+ chdir: bool = False,
106
+ run: Run | None = None,
79
107
  run_id: str | None = None,
80
108
  experiment_id: str | None = None,
81
109
  run_name: str | None = None,
@@ -93,6 +121,9 @@ def start_run( # noqa: PLR0913
93
121
 
94
122
  Args:
95
123
  config (object): The configuration object to log parameters from.
124
+ chdir (bool): Whether to change the current working directory to the
125
+ artifact directory of the current run. Defaults to False.
126
+ run (Run | None): The existing run. Defaults to None.
96
127
  run_id (str | None): The existing run ID. Defaults to None.
97
128
  experiment_id (str | None): The experiment ID. Defaults to None.
98
129
  run_name (str | None): The name of the run. Defaults to None.
@@ -117,7 +148,11 @@ def start_run( # noqa: PLR0913
117
148
  - `mlflow.start_run`: The MLflow function to start a run directly.
118
149
  - `log_run`: A context manager to log parameters and manage the MLflow
119
150
  run context.
151
+
120
152
  """
153
+ if run:
154
+ run_id = run.info.run_id
155
+
121
156
  with (
122
157
  mlflow.start_run(
123
158
  run_id=run_id,
@@ -131,7 +166,11 @@ def start_run( # noqa: PLR0913
131
166
  ) as run,
132
167
  log_run(config if run_id is None else None, synchronous=synchronous),
133
168
  ):
134
- yield run
169
+ if chdir:
170
+ with chdir_artifact(run):
171
+ yield run
172
+ else:
173
+ yield run
135
174
 
136
175
 
137
176
  @contextmanager
@@ -167,6 +206,7 @@ def chdir_artifact(
167
206
  Args:
168
207
  run (Run): The run to get the artifact directory from.
169
208
  artifact_path (str | None): The artifact path.
209
+
170
210
  """
171
211
  curdir = Path.cwd()
172
212
  path = mlflow.artifacts.download_artifacts(
hydraflow/mlflow.py CHANGED
@@ -54,6 +54,7 @@ def set_experiment(
54
54
  Returns:
55
55
  Experiment: An instance of `mlflow.entities.Experiment` representing
56
56
  the new active experiment.
57
+
57
58
  """
58
59
  if uri is not None:
59
60
  mlflow.set_tracking_uri(uri)
@@ -77,6 +78,7 @@ def log_params(config: object, *, synchronous: bool | None = None) -> None:
77
78
  config (object): The configuration object to log the parameters from.
78
79
  synchronous (bool | None): Whether to log the parameters synchronously.
79
80
  Defaults to None.
81
+
80
82
  """
81
83
  for key, value in iter_params(config):
82
84
  mlflow.log_param(key, value, synchronous=synchronous)
@@ -133,6 +135,7 @@ def search_runs( # noqa: PLR0913
133
135
 
134
136
  Returns:
135
137
  A `RunCollection` object containing the search results.
138
+
136
139
  """
137
140
  runs = mlflow.search_runs(
138
141
  experiment_ids=experiment_ids,
@@ -177,6 +180,7 @@ def list_runs(
177
180
  Returns:
178
181
  RunCollection: A `RunCollection` instance containing the runs for the
179
182
  specified experiments.
183
+
180
184
  """
181
185
  rc = _list_runs(experiment_names, n_jobs)
182
186
  if status is None:
hydraflow/param.py CHANGED
@@ -28,6 +28,7 @@ def match(param: str, value: Any) -> bool: # noqa: PLR0911
28
28
  Returns:
29
29
  True if the parameter matches the specified value,
30
30
  False otherwise.
31
+
31
32
  """
32
33
  if callable(value):
33
34
  return value(param)
@@ -94,6 +95,7 @@ def to_value(param: str | None, type_: type) -> Any:
94
95
 
95
96
  Returns:
96
97
  The converted value.
98
+
97
99
  """
98
100
  if param is None or param == "None":
99
101
  return None
@@ -129,6 +131,7 @@ def get_params(run: Run, *names: str | list[str]) -> tuple[str | None, ...]:
129
131
  Returns:
130
132
  tuple[str | None, ...]: A tuple containing the values of the specified
131
133
  parameters in the order they were provided.
134
+
132
135
  """
133
136
  names_ = []
134
137
  for name in names:
@@ -155,6 +158,7 @@ def get_values(run: Run, names: list[str], types: list[type]) -> tuple[Any, ...]
155
158
  Returns:
156
159
  tuple[Any, ...]: A tuple containing the values of the specified
157
160
  parameters in the order they were provided.
161
+
158
162
  """
159
163
  params = get_params(run, names)
160
164
  it = zip(params, types, strict=True)
@@ -106,6 +106,7 @@ class RunCollection:
106
106
 
107
107
  Returns:
108
108
  A new `RunCollection` instance with the runs from both collections.
109
+
109
110
  """
110
111
  return self.__class__(self._runs + other._runs)
111
112
 
@@ -118,6 +119,7 @@ class RunCollection:
118
119
  Returns:
119
120
  A new `RunCollection` instance with the runs that are in this collection
120
121
  but not in the other.
122
+
121
123
  """
122
124
  runs = [run for run in self._runs if run not in other._runs] # noqa: SLF001
123
125
  return self.__class__(runs)
@@ -150,6 +152,7 @@ class RunCollection:
150
152
  Returns:
151
153
  A new `RunCollection` instance containing the first n runs if n is
152
154
  positive, or the last n runs if n is negative.
155
+
153
156
  """
154
157
  if n < 0:
155
158
  return self.__class__(self._runs[n:])
@@ -164,6 +167,7 @@ class RunCollection:
164
167
 
165
168
  Raises:
166
169
  ValueError: If the collection does not contain exactly one run.
170
+
167
171
  """
168
172
  if len(self._runs) != 1:
169
173
  raise ValueError("The collection does not contain exactly one run.")
@@ -176,6 +180,7 @@ class RunCollection:
176
180
  Returns:
177
181
  The only `Run` instance in the collection, or None if the collection
178
182
  does not contain exactly one run.
183
+
179
184
  """
180
185
  return self._runs[0] if len(self._runs) == 1 else None
181
186
 
@@ -187,6 +192,7 @@ class RunCollection:
187
192
 
188
193
  Raises:
189
194
  ValueError: If the collection is empty.
195
+
190
196
  """
191
197
  if not self._runs:
192
198
  raise ValueError("The collection is empty.")
@@ -199,6 +205,7 @@ class RunCollection:
199
205
  Returns:
200
206
  The first `Run` instance in the collection, or None if the collection
201
207
  is empty.
208
+
202
209
  """
203
210
  return self._runs[0] if self._runs else None
204
211
 
@@ -210,6 +217,7 @@ class RunCollection:
210
217
 
211
218
  Raises:
212
219
  ValueError: If the collection is empty.
220
+
213
221
  """
214
222
  if not self._runs:
215
223
  raise ValueError("The collection is empty.")
@@ -222,6 +230,7 @@ class RunCollection:
222
230
  Returns:
223
231
  The last `Run` instance in the collection, or None if the collection
224
232
  is empty.
233
+
225
234
  """
226
235
  return self._runs[-1] if self._runs else None
227
236
 
@@ -262,6 +271,7 @@ class RunCollection:
262
271
 
263
272
  Returns:
264
273
  A new `RunCollection` object containing the filtered runs.
274
+
265
275
  """
266
276
  return RunCollection(
267
277
  filter_runs(
@@ -294,6 +304,7 @@ class RunCollection:
294
304
 
295
305
  See Also:
296
306
  `filter`: Perform the actual filtering logic.
307
+
297
308
  """
298
309
  try:
299
310
  return self.filter(config, **kwargs).first()
@@ -318,6 +329,7 @@ class RunCollection:
318
329
 
319
330
  See Also:
320
331
  `filter`: Perform the actual filtering logic.
332
+
321
333
  """
322
334
  return self.filter(config, **kwargs).try_first()
323
335
 
@@ -341,6 +353,7 @@ class RunCollection:
341
353
 
342
354
  See Also:
343
355
  `filter`: Perform the actual filtering logic.
356
+
344
357
  """
345
358
  try:
346
359
  return self.filter(config, **kwargs).last()
@@ -365,6 +378,7 @@ class RunCollection:
365
378
 
366
379
  See Also:
367
380
  `filter`: Perform the actual filtering logic.
381
+
368
382
  """
369
383
  return self.filter(config, **kwargs).try_last()
370
384
 
@@ -389,6 +403,7 @@ class RunCollection:
389
403
 
390
404
  See Also:
391
405
  `filter`: Perform the actual filtering logic.
406
+
392
407
  """
393
408
  try:
394
409
  return self.filter(config, **kwargs).one()
@@ -417,6 +432,7 @@ class RunCollection:
417
432
 
418
433
  See Also:
419
434
  `filter`: Perform the actual filtering logic.
435
+
420
436
  """
421
437
  return self.filter(config, **kwargs).try_one()
422
438
 
@@ -429,6 +445,7 @@ class RunCollection:
429
445
 
430
446
  Returns:
431
447
  A list of unique parameter names.
448
+
432
449
  """
433
450
  param_names = set()
434
451
 
@@ -453,6 +470,7 @@ class RunCollection:
453
470
  Returns:
454
471
  A dictionary where the keys are parameter names and the values are
455
472
  lists of parameter values.
473
+
456
474
  """
457
475
  params = {}
458
476
 
@@ -484,6 +502,7 @@ class RunCollection:
484
502
 
485
503
  Yields:
486
504
  Results obtained by applying the function to each run in the collection.
505
+
487
506
  """
488
507
  return (func(run, *args, **kwargs) for run in self)
489
508
 
@@ -504,6 +523,7 @@ class RunCollection:
504
523
  Yields:
505
524
  Results obtained by applying the function to each run id in the
506
525
  collection.
526
+
507
527
  """
508
528
  return (func(run_id, *args, **kwargs) for run_id in self.info.run_id)
509
529
 
@@ -524,6 +544,7 @@ class RunCollection:
524
544
  Yields:
525
545
  Results obtained by applying the function to each run configuration
526
546
  in the collection.
547
+
527
548
  """
528
549
  return (func(load_config(run), *args, **kwargs) for run in self)
529
550
 
@@ -548,6 +569,7 @@ class RunCollection:
548
569
  Yields:
549
570
  Results obtained by applying the function to each artifact URI in the
550
571
  collection.
572
+
551
573
  """
552
574
  return (func(uri, *args, **kwargs) for uri in self.info.artifact_uri)
553
575
 
@@ -571,6 +593,7 @@ class RunCollection:
571
593
  Yields:
572
594
  Results obtained by applying the function to each artifact directory
573
595
  in the collection.
596
+
574
597
  """
575
598
  return (func(dir, *args, **kwargs) for dir in self.info.artifact_dir) # noqa: A001
576
599
 
@@ -594,6 +617,7 @@ class RunCollection:
594
617
  dictionary where the keys are tuples of parameter values and the
595
618
  values are `RunCollection` objects containing the runs that match
596
619
  those parameter values.
620
+
597
621
  """
598
622
  grouped_runs: dict[str | None | tuple[str | None, ...], list[Run]] = {}
599
623
  is_list = isinstance(names, list)
@@ -620,6 +644,7 @@ class RunCollection:
620
644
  key (Callable[[Run], Any] | None): A function that takes a run and returns
621
645
  a value to sort by.
622
646
  reverse (bool): If True, sort in descending order.
647
+
623
648
  """
624
649
  self._runs.sort(key=key or (lambda x: x.info.start_time), reverse=reverse)
625
650
 
@@ -633,6 +658,7 @@ class RunCollection:
633
658
 
634
659
  Returns:
635
660
  A list of values for the specified parameters.
661
+
636
662
  """
637
663
  is_list = isinstance(names, list)
638
664
 
@@ -664,6 +690,7 @@ class RunCollection:
664
690
  This can be a single parameter name or multiple names provided
665
691
  as separate arguments or as a list.
666
692
  reverse (bool): If True, sort in descending order.
693
+
667
694
  """
668
695
  values = self.values(names)
669
696
  index = sorted(range(len(self)), key=lambda i: values[i], reverse=reverse)
@@ -721,6 +748,7 @@ def filter_runs(
721
748
 
722
749
  Returns:
723
750
  A list of runs that match the specified configuration and key-value pairs.
751
+
724
752
  """
725
753
  if override:
726
754
  config = select_overrides(config)
@@ -751,6 +779,7 @@ def filter_runs_by_status(
751
779
 
752
780
  Returns:
753
781
  A list of runs that match the specified status.
782
+
754
783
  """
755
784
  if isinstance(status, str):
756
785
  if status.startswith("!"):
hydraflow/run_data.py CHANGED
@@ -37,6 +37,7 @@ class RunCollectionData:
37
37
 
38
38
  Returns:
39
39
  A DataFrame containing the runs' configurations.
40
+
40
41
  """
41
42
  return DataFrame(self._runs.map_config(collect_params))
42
43
 
hydraflow/utils.py CHANGED
@@ -26,6 +26,7 @@ def get_artifact_dir(run: Run | None = None) -> Path:
26
26
 
27
27
  Returns:
28
28
  The local path to the directory where the artifacts are downloaded.
29
+
29
30
  """
30
31
  uri = mlflow.get_artifact_uri() if run is None else run.info.artifact_uri
31
32
 
@@ -52,6 +53,7 @@ def get_artifact_path(run: Run | None, path: str) -> Path:
52
53
 
53
54
  Returns:
54
55
  The local path to the artifact.
56
+
55
57
  """
56
58
  return get_artifact_dir(run) / path
57
59
 
@@ -74,6 +76,7 @@ def get_hydra_output_dir(run: Run | None = None) -> Path:
74
76
  Raises:
75
77
  FileNotFoundError: If the Hydra configuration file is not found
76
78
  in the artifacts.
79
+
77
80
  """
78
81
  if run is None:
79
82
  hc = HydraConfig.get()
@@ -102,6 +105,7 @@ def load_config(run: Run) -> DictConfig:
102
105
  Returns:
103
106
  The loaded configuration as a DictConfig object. Returns an empty
104
107
  DictConfig if the configuration file is not found.
108
+
105
109
  """
106
110
  path = get_artifact_dir(run) / ".hydra/config.yaml"
107
111
  return OmegaConf.load(path) # type: ignore
@@ -126,6 +130,7 @@ def load_overrides(run: Run) -> list[str]:
126
130
  Returns:
127
131
  The loaded overrides as a list of strings. Returns an empty list
128
132
  if the overrides file is not found.
133
+
129
134
  """
130
135
  path = get_artifact_dir(run) / ".hydra/overrides.yaml"
131
136
  return [str(x) for x in OmegaConf.load(path)]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.5.4
3
+ Version: 0.6.0
4
4
  Summary: Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments.
5
5
  Project-URL: Documentation, https://daizutabi.github.io/hydraflow/
6
6
  Project-URL: Source, https://github.com/daizutabi/hydraflow
@@ -0,0 +1,14 @@
1
+ hydraflow/__init__.py,sha256=9XO9FD3uiTTPN6X6UAC9FtkJjEqUQZNqpoAmSrjUHfI,855
2
+ hydraflow/config.py,sha256=MNX9da5bPVDcjnpji7Cm9ndK6ura92pt361m4PRh6_E,4326
3
+ hydraflow/context.py,sha256=rc43zvE2ueki0zEzorCMIthD9cho_PkbLLJYF9WgDqY,6562
4
+ hydraflow/mlflow.py,sha256=h2S_A2wElr_1lAq0D1wkoEfdtDZpPuWFNRcO8mV_VrA,8932
5
+ hydraflow/param.py,sha256=yu1aMNXRLegXGDL-68vwIkfeDF9CaU784WZENGLwl7Q,4572
6
+ hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ hydraflow/run_collection.py,sha256=2GRVOy87_2SPjHuCzzUvRNugO_grtFUVjtTfhznwBAc,27444
8
+ hydraflow/run_data.py,sha256=dpyyfnuH9mCtIZeigMo1iFQo9bafMdEL4i4uI2l0UqY,1525
9
+ hydraflow/run_info.py,sha256=Jf5wrIjRLIV1-k-obHDqwKHa6j_ZonrY8od-rXlbtMo,1024
10
+ hydraflow/utils.py,sha256=oXjcyfQBbPzJNTh3_CbZfl23zgJS-mbNM9GAWBwsn8c,4349
11
+ hydraflow-0.6.0.dist-info/METADATA,sha256=xUib1EsbG3Es5jFx0cSkF1QItfTuciBHYM1040GqFzA,4700
12
+ hydraflow-0.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ hydraflow-0.6.0.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
14
+ hydraflow-0.6.0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- hydraflow/__init__.py,sha256=9XO9FD3uiTTPN6X6UAC9FtkJjEqUQZNqpoAmSrjUHfI,855
2
- hydraflow/config.py,sha256=k6pwneDoC4mb7cVkafMfITn8QemICPnFbhIRGLsmF4w,4323
3
- hydraflow/context.py,sha256=KukvVexCF32myBOryYMVhINknbAgPFcanmr4cT2EXog,5521
4
- hydraflow/mlflow.py,sha256=TVp6O9rYNYcdqnlssstXQlI_P_N6inUG-F01X6uJNv4,8928
5
- hydraflow/param.py,sha256=kuVlxEfgoKL35rNu49M-PkdNr41djK0YZ5IO7fHTSSU,4568
6
- hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- hydraflow/run_collection.py,sha256=mMGpr6cAh9Bf2SeRsYNHySOQJNrWNMI7esDWzyMxnJg,27415
8
- hydraflow/run_data.py,sha256=V_yhkBsJlDTkLbh1TShkvDxUQYUYTpXFahXJGfdmgro,1524
9
- hydraflow/run_info.py,sha256=Jf5wrIjRLIV1-k-obHDqwKHa6j_ZonrY8od-rXlbtMo,1024
10
- hydraflow/utils.py,sha256=ha-gSq3TkWWOlKmnDuyLNCuKNQdEG08CXRzKtb3C4mk,4344
11
- hydraflow-0.5.4.dist-info/METADATA,sha256=ijJv5powqpMjZ1adDCWdLxRLeEaosmKF3gG74L25fCs,4700
12
- hydraflow-0.5.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- hydraflow-0.5.4.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
14
- hydraflow-0.5.4.dist-info/RECORD,,