hydraflow 0.9.1__py3-none-any.whl → 0.10.1__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/core/io.py CHANGED
@@ -136,7 +136,7 @@ def load_overrides(run: Run) -> ListConfig:
136
136
 
137
137
  """
138
138
  path = get_artifact_dir(run) / ".hydra/overrides.yaml"
139
- return OmegaConf.load(path) # type: ignore
139
+ return sorted(OmegaConf.load(path)) # type: ignore
140
140
 
141
141
 
142
142
  def remove_run(run: Run | Iterable[Run]) -> None:
hydraflow/core/main.py CHANGED
@@ -154,11 +154,15 @@ def equals(run_dir: Path, config: Any, overrides: list[str] | None) -> bool:
154
154
  """
155
155
  if overrides is None:
156
156
  path = run_dir / "artifacts/.hydra/config.yaml"
157
- else:
158
- path = run_dir / "artifacts/.hydra/overrides.yaml"
159
- config = overrides
157
+
158
+ if not path.exists():
159
+ return False
160
+
161
+ return OmegaConf.load(path) == config
162
+
163
+ path = run_dir / "artifacts/.hydra/overrides.yaml"
160
164
 
161
165
  if not path.exists():
162
166
  return False
163
167
 
164
- return OmegaConf.load(path) == config
168
+ return sorted(OmegaConf.load(path)) == sorted(overrides)
@@ -116,7 +116,7 @@ class RunCollection:
116
116
  but not in the other.
117
117
 
118
118
  """
119
- runs = [run for run in self._runs if run not in other._runs] # noqa: SLF001
119
+ runs = [run for run in self._runs if run not in other._runs]
120
120
  return self.__class__(runs)
121
121
 
122
122
  @property
hydraflow/executor/job.py CHANGED
@@ -71,7 +71,7 @@ def iter_batches(job: Job) -> Iterator[list[str]]:
71
71
 
72
72
  for step in job.steps:
73
73
  for args in iter_args(step):
74
- sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ulid()}"
74
+ sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ULID()}"
75
75
  yield ["--multirun", sweep_dir, job_name, *args]
76
76
 
77
77
 
@@ -99,6 +99,7 @@ def multirun(job: Job) -> None:
99
99
 
100
100
  if job.run:
101
101
  base_cmds = shlex.split(job.run)
102
+
102
103
  for args in it:
103
104
  cmds = [*base_cmds, *args]
104
105
  try:
@@ -108,22 +109,24 @@ def multirun(job: Job) -> None:
108
109
  raise RuntimeError(msg) from e
109
110
 
110
111
  elif job.call:
111
- if "." not in job.call:
112
- msg = f"Invalid function path: {job.call}."
112
+ call_name, *base_args = shlex.split(job.call)
113
+
114
+ if "." not in call_name:
115
+ msg = f"Invalid function path: {call_name}."
113
116
  msg += " Expected format: 'package.module.function'"
114
117
  raise ValueError(msg)
115
118
 
116
119
  try:
117
- module_name, func_name = job.call.rsplit(".", 1)
120
+ module_name, func_name = call_name.rsplit(".", 1)
118
121
  module = importlib.import_module(module_name)
119
122
  func = getattr(module, func_name)
120
123
  except (ImportError, AttributeError, ModuleNotFoundError) as e:
121
- msg = f"Failed to import or find function: {job.call}"
124
+ msg = f"Failed to import or find function: {call_name}"
122
125
  raise ValueError(msg) from e
123
126
 
124
127
  for args in it:
125
128
  try:
126
- func(*args)
129
+ func([*base_args, *args])
127
130
  except Exception as e: # noqa: PERF203
128
131
  msg = f"Function call '{job.call}' failed with args: {args}"
129
132
  raise RuntimeError(msg) from e
@@ -8,7 +8,7 @@ ranges, and expand values from string arguments.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- import re
11
+ import shlex
12
12
  from itertools import chain, product
13
13
  from typing import TYPE_CHECKING
14
14
 
@@ -289,7 +289,7 @@ def split(arg: str) -> list[str]:
289
289
  return result
290
290
 
291
291
 
292
- def expand_values(arg: str) -> list[str]:
292
+ def expand_values(arg: str, suffix: str = "") -> Iterator[str]:
293
293
  """Expand a string argument into a list of values.
294
294
 
295
295
  Take a string containing comma-separated values or ranges and return a list
@@ -297,12 +297,36 @@ def expand_values(arg: str) -> list[str]:
297
297
 
298
298
  Args:
299
299
  arg (str): The argument to expand.
300
+ suffix (str): The suffix to append to each value.
300
301
 
301
302
  Returns:
302
- list[str]: A list of the expanded values.
303
+ Iterator[str]: An iterator of the expanded values.
303
304
 
304
305
  """
305
- return list(chain.from_iterable(collect_values(x) for x in split(arg)))
306
+ if suffix in SUFFIX_EXPONENT:
307
+ suffix = SUFFIX_EXPONENT[suffix]
308
+
309
+ for value in chain.from_iterable(collect_values(x) for x in split(arg)):
310
+ yield f"{value}{suffix}"
311
+
312
+
313
+ def split_arg(arg: str) -> tuple[str, str, str]:
314
+ """Split an argument into a key, suffix, and value.
315
+
316
+ Args:
317
+ arg (str): The argument to split.
318
+
319
+ Returns:
320
+ tuple[str, str, str]: A tuple containing the key, suffix, and value.
321
+
322
+ """
323
+ key, value = arg.split("=")
324
+
325
+ if "/" in key:
326
+ key, suffix = key.split("/", 1)
327
+ return key, suffix, value
328
+
329
+ return key, "", value
306
330
 
307
331
 
308
332
  def collect_arg(arg: str) -> str:
@@ -318,9 +342,9 @@ def collect_arg(arg: str) -> str:
318
342
  str: A string of the collected key and values.
319
343
 
320
344
  """
321
- key, arg = arg.split("=")
322
- arg = ",".join(expand_values(arg))
323
- return f"{key}={arg}"
345
+ key, suffix, value = split_arg(arg)
346
+ value = ",".join(expand_values(value, suffix))
347
+ return f"{key}={value}"
324
348
 
325
349
 
326
350
  def expand_arg(arg: str) -> Iterator[str]:
@@ -338,26 +362,27 @@ def expand_arg(arg: str) -> Iterator[str]:
338
362
 
339
363
  """
340
364
  if "|" not in arg:
341
- key, value = arg.split("=")
365
+ key, suffix, value = split_arg(arg)
342
366
 
343
- for v in expand_values(value):
367
+ for v in expand_values(value, suffix):
344
368
  yield f"{key}={v}"
345
369
 
346
370
  return
347
371
 
348
372
  args = arg.split("|")
349
373
  key = ""
374
+ suffix = ""
350
375
 
351
376
  for arg_ in args:
352
377
  if "=" in arg_:
353
- key, value = arg_.split("=")
378
+ key, suffix, value = split_arg(arg_)
354
379
  elif key:
355
380
  value = arg_
356
381
  else:
357
382
  msg = f"Invalid argument: {arg_}"
358
383
  raise ValueError(msg)
359
384
 
360
- value = ",".join(expand_values(value))
385
+ value = ",".join(expand_values(value, suffix))
361
386
  yield f"{key}={value}"
362
387
 
363
388
 
@@ -372,7 +397,7 @@ def collect(args: str | list[str]) -> list[str]:
372
397
 
373
398
  """
374
399
  if isinstance(args, str):
375
- args = re.split(r"\s+", args.strip())
400
+ args = shlex.split(args)
376
401
 
377
402
  args = [arg for arg in args if "=" in arg]
378
403
 
@@ -390,7 +415,7 @@ def expand(args: str | list[str]) -> list[list[str]]:
390
415
 
391
416
  """
392
417
  if isinstance(args, str):
393
- args = re.split(r"\s+", args.strip())
418
+ args = shlex.split(args)
394
419
 
395
420
  args = [arg for arg in args if "=" in arg]
396
421
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.9.1
3
+ Version: 0.10.1
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
@@ -39,9 +39,9 @@ Requires-Python: >=3.10
39
39
  Requires-Dist: hydra-core>=1.3
40
40
  Requires-Dist: mlflow>=2.15
41
41
  Requires-Dist: omegaconf
42
+ Requires-Dist: python-ulid>=3.0.0
42
43
  Requires-Dist: rich
43
44
  Requires-Dist: typer
44
- Requires-Dist: ulid
45
45
  Description-Content-Type: text/markdown
46
46
 
47
47
  # Hydraflow
@@ -4,21 +4,21 @@ hydraflow/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  hydraflow/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  hydraflow/core/config.py,sha256=SJzjgsO_kzB78_whJ3lmy7GlZvTvwZONH1BJBn8zCuI,3817
6
6
  hydraflow/core/context.py,sha256=QPyPg1xrTlmhviKNn-0nDY9bXcVky1zInqRqPN-VNhc,4741
7
- hydraflow/core/io.py,sha256=KK9_mkx4XnBgA63Ab-g833N5q9aCsSrJR-7pp6unEKw,6746
8
- hydraflow/core/main.py,sha256=gYb1OOVH0CL4385Dm-06Mqi1Mr9-24URwLUiW86pGNs,5018
7
+ hydraflow/core/io.py,sha256=C207DsGAU4CLvlHySyyIl0_aKZ83ysJ-W_wNM2n4RPI,6754
8
+ hydraflow/core/main.py,sha256=500fh5cnlKj9CHFgD8-f16wZcOuPF-5zSvFeVMqI8MQ,5100
9
9
  hydraflow/core/mlflow.py,sha256=M3MhiChnMzKnKRmjBl4h_SRGkAZKL7GAmFr3DdzwRuQ,5666
10
10
  hydraflow/core/param.py,sha256=LHU9j9_7oA99igasoOyKofKClVr9FmGA3UABJ-KmyS0,4538
11
11
  hydraflow/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- hydraflow/entities/run_collection.py,sha256=BHbMvYB4WyG5qSNYpIzwmHySOtmGNb-jg2OrCaTPr2I,19651
12
+ hydraflow/entities/run_collection.py,sha256=E8IRBgxCnJE_IPCaSmS2mc9GtDXXLBfc7GHv07d2j98,19635
13
13
  hydraflow/entities/run_data.py,sha256=Y2_Lc-BdQ7nXhcEIjdHGHIkLrXsmAktOftESEwYOY8o,1602
14
14
  hydraflow/entities/run_info.py,sha256=FRC6ICOlzB2u_xi_33Qs-YZLt677UotuNbYqI7XSmHY,1017
15
15
  hydraflow/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  hydraflow/executor/conf.py,sha256=q_FrPXQJCVGKS1FYnGRGqTUgMQeMBkaVPW2mtQc8oxk,384
17
17
  hydraflow/executor/io.py,sha256=4nafwge6vHanYFuEHxd0LRv_3ZLgMpV50qSbssZNe3Q,696
18
- hydraflow/executor/job.py,sha256=Vp2IZOuIC25Gqo9A_5MkllWl1T1QBfUnI5ksMvKwakg,4479
19
- hydraflow/executor/parser.py,sha256=y4C9wVdUnazJDxdWrT5y3yWFIo0zAGzO-cS9x1MTK_8,9486
20
- hydraflow-0.9.1.dist-info/METADATA,sha256=Lf2rS7gDBJ-4s4lPtsCg2-S8mSIlZoJoyVJarel_pQk,4559
21
- hydraflow-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- hydraflow-0.9.1.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
- hydraflow-0.9.1.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
- hydraflow-0.9.1.dist-info/RECORD,,
18
+ hydraflow/executor/job.py,sha256=_f_7lfPsPMGBNB7ZW50-MCZX-BQi-s0_Ajg3DjqHx5s,4553
19
+ hydraflow/executor/parser.py,sha256=MO8VU0uVQZeku6kbw8Urid_5QEcnR8atd5h-yDP5OhQ,10147
20
+ hydraflow-0.10.1.dist-info/METADATA,sha256=1fBBNz3-3gczIXYs9tq7lvzDySjz1BlFMQsO73KgCrg,4574
21
+ hydraflow-0.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ hydraflow-0.10.1.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
+ hydraflow-0.10.1.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
+ hydraflow-0.10.1.dist-info/RECORD,,