hydraflow 0.5.4__py3-none-any.whl → 0.6.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,