hydraflow 0.11.0__py3-none-any.whl → 0.12.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.
@@ -7,7 +7,7 @@ from dataclasses import dataclass, field
7
7
  class Step:
8
8
  batch: str = ""
9
9
  args: str = ""
10
- configs: str = ""
10
+ with_: str = ""
11
11
 
12
12
 
13
13
  @dataclass
@@ -15,7 +15,7 @@ class Job:
15
15
  name: str = ""
16
16
  run: str = ""
17
17
  call: str = ""
18
- configs: str = ""
18
+ with_: str = ""
19
19
  steps: list[Step] = field(default_factory=list)
20
20
 
21
21
 
hydraflow/executor/io.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from omegaconf import OmegaConf
8
+ from omegaconf import DictConfig, ListConfig, OmegaConf
9
9
 
10
10
  from .conf import HydraflowConf
11
11
 
@@ -35,7 +35,26 @@ def load_config() -> HydraflowConf:
35
35
 
36
36
  cfg = OmegaConf.load(path)
37
37
 
38
- return OmegaConf.merge(schema, cfg) # type: ignore
38
+ if not isinstance(cfg, DictConfig):
39
+ return schema
40
+
41
+ rename_with(cfg)
42
+
43
+ return OmegaConf.merge(schema, cfg) # type: ignore[return-value]
44
+
45
+
46
+ def rename_with(cfg: DictConfig) -> None:
47
+ """Rename the `with` field to `with_`."""
48
+ if "with" in cfg:
49
+ cfg["with_"] = cfg.pop("with")
50
+
51
+ for key in list(cfg.keys()):
52
+ if isinstance(cfg[key], DictConfig):
53
+ rename_with(cfg[key])
54
+ elif isinstance(cfg[key], ListConfig):
55
+ for item in cfg[key]:
56
+ if isinstance(item, DictConfig):
57
+ rename_with(item)
39
58
 
40
59
 
41
60
  def get_job(name: str) -> Job:
hydraflow/executor/job.py CHANGED
@@ -69,10 +69,10 @@ def iter_batches(job: Job) -> Iterator[list[str]]:
69
69
 
70
70
  """
71
71
  job_name = f"hydra.job.name={job.name}"
72
- job_configs = shlex.split(job.configs)
72
+ job_configs = shlex.split(job.with_)
73
73
 
74
74
  for step in job.steps:
75
- configs = shlex.split(step.configs) or job_configs
75
+ configs = shlex.split(step.with_) or job_configs
76
76
 
77
77
  for args in iter_args(step.batch, step.args):
78
78
  sweep_dir = f"hydra.sweep.dir=multirun/{ulid.ULID()}"
@@ -216,6 +216,9 @@ def collect_values(arg: str) -> list[str]:
216
216
  list[str]: A list of the collected values.
217
217
 
218
218
  """
219
+ if "(" in arg:
220
+ return collect_parentheses(arg)
221
+
219
222
  if ":" not in arg:
220
223
  return [arg]
221
224
 
@@ -235,6 +238,58 @@ def collect_values(arg: str) -> list[str]:
235
238
  return [add_exponent(x, exponent) for x in values]
236
239
 
237
240
 
241
+ def split_parentheses(arg: str) -> Iterator[str]:
242
+ """Split a string with parentheses into a list of strings.
243
+
244
+ Args:
245
+ arg (str): The string to split.
246
+
247
+ Returns:
248
+ Iterator[str]: An iterator of the split strings.
249
+
250
+ Examples:
251
+ >>> list(split_parentheses("a(b,c)m(e:f)k"))
252
+ ['a', 'b,c', 'e-3', 'e:f', 'e3']
253
+ >>> list(split_parentheses("(b,c)d(e:f)"))
254
+ ['b,c', 'd', 'e:f']
255
+
256
+ """
257
+ current = ""
258
+
259
+ for char in arg:
260
+ if char in ("(", ")"):
261
+ if current:
262
+ yield SUFFIX_EXPONENT.get(current, current)
263
+ current = ""
264
+ else:
265
+ current += char
266
+
267
+ if current:
268
+ yield SUFFIX_EXPONENT.get(current, current)
269
+
270
+
271
+ def collect_parentheses(arg: str) -> list[str]:
272
+ """Collect values from a string with parentheses.
273
+
274
+ Args:
275
+ arg (str): The string to collect values from.
276
+
277
+ Returns:
278
+ list[str]: A list of the collected values.
279
+
280
+ Examples:
281
+ >>> collect_parentheses("(1:3,5:2:9,20)k")
282
+ ['1e3', '2e3', '3e3', '5e3', '7e3', '9e3', '20e3']
283
+ >>> collect_parentheses("2e(-1,-2,-3)")
284
+ ['2e-1', '2e-2', '2e-3']
285
+ >>> collect_parentheses("(1:3)e(3,5)")
286
+ ['1e3', '2e3', '3e3', '1e5', '2e5', '3e5']
287
+
288
+ """
289
+ it = [expand_values(x) for x in split_parentheses(arg)]
290
+ return ["".join(x[::-1]) for x in product(*it[::-1])]
291
+
292
+
238
293
  def split(arg: str) -> list[str]:
239
294
  r"""Split a string by top-level commas.
240
295
 
@@ -255,11 +310,14 @@ def split(arg: str) -> list[str]:
255
310
  ['"x,y"', 'z']
256
311
  >>> split("'p,q',r")
257
312
  ["'p,q'", 'r']
313
+ >>> split("(a,b)m,(1,2:4)k")
314
+ ['(a,b)m', '(1,2:4)k']
258
315
 
259
316
  """
260
317
  result = []
261
318
  current = []
262
319
  bracket_count = 0
320
+ paren_count = 0
263
321
  in_single_quote = False
264
322
  in_double_quote = False
265
323
 
@@ -272,9 +330,14 @@ def split(arg: str) -> list[str]:
272
330
  bracket_count += 1
273
331
  elif char == "]" and not (in_single_quote or in_double_quote):
274
332
  bracket_count -= 1
333
+ elif char == "(" and not (in_single_quote or in_double_quote):
334
+ paren_count += 1
335
+ elif char == ")" and not (in_single_quote or in_double_quote):
336
+ paren_count -= 1
275
337
  elif (
276
338
  char == ","
277
339
  and bracket_count == 0
340
+ and paren_count == 0
278
341
  and not in_single_quote
279
342
  and not in_double_quote
280
343
  ):
@@ -303,8 +366,7 @@ def expand_values(arg: str, suffix: str = "") -> Iterator[str]:
303
366
  Iterator[str]: An iterator of the expanded values.
304
367
 
305
368
  """
306
- if suffix in SUFFIX_EXPONENT:
307
- suffix = SUFFIX_EXPONENT[suffix]
369
+ suffix = SUFFIX_EXPONENT.get(suffix, suffix)
308
370
 
309
371
  for value in chain.from_iterable(collect_values(x) for x in split(arg)):
310
372
  yield f"{value}{suffix}"
@@ -320,10 +382,14 @@ def split_arg(arg: str) -> tuple[str, str, str]:
320
382
  tuple[str, str, str]: A tuple containing the key, suffix, and value.
321
383
 
322
384
  """
385
+ if "=" not in arg:
386
+ msg = f"Invalid argument: {arg}"
387
+ raise ValueError(msg)
388
+
323
389
  key, value = arg.split("=")
324
390
 
325
391
  if "/" in key:
326
- key, suffix = key.split("/", 1)
392
+ key, suffix = key.split("/")
327
393
  return key, suffix, value
328
394
 
329
395
  return key, "", value
@@ -399,8 +465,6 @@ def collect(args: str | list[str]) -> list[str]:
399
465
  if isinstance(args, str):
400
466
  args = shlex.split(args)
401
467
 
402
- args = [arg for arg in args if "=" in arg]
403
-
404
468
  return [collect_arg(arg) for arg in args]
405
469
 
406
470
 
@@ -417,6 +481,4 @@ def expand(args: str | list[str]) -> list[list[str]]:
417
481
  if isinstance(args, str):
418
482
  args = shlex.split(args)
419
483
 
420
- args = [arg for arg in args if "=" in arg]
421
-
422
484
  return [list(x) for x in product(*(expand_arg(arg) for arg in args))]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -97,7 +97,6 @@ Here is a simple example to get you started with Hydraflow:
97
97
  from __future__ import annotations
98
98
 
99
99
  from dataclasses import dataclass
100
- from pathlib import Path
101
100
  from typing import TYPE_CHECKING
102
101
 
103
102
  import hydraflow
@@ -13,12 +13,12 @@ hydraflow/entities/run_collection.py,sha256=E8IRBgxCnJE_IPCaSmS2mc9GtDXXLBfc7GHv
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
- hydraflow/executor/conf.py,sha256=SJNiQ87MXMlpDfdm0POcv55MY3GS5FUh5wT7u3XU3oU,406
17
- hydraflow/executor/io.py,sha256=xV3m-nV9eKbu9Fb7u04J2bfmR_Ky3jTEJjq4QC2m6V4,954
18
- hydraflow/executor/job.py,sha256=yiVbAYgsZZtSTRER-H5pUopULeygzPNzKlSkl27yFI4,4856
19
- hydraflow/executor/parser.py,sha256=MO8VU0uVQZeku6kbw8Urid_5QEcnR8atd5h-yDP5OhQ,10147
20
- hydraflow-0.11.0.dist-info/METADATA,sha256=MIDZ7Vec2SDAcj0S1SvghtlN5RQTpfR1N3MLlwoLD9A,4574
21
- hydraflow-0.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- hydraflow-0.11.0.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
- hydraflow-0.11.0.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
- hydraflow-0.11.0.dist-info/RECORD,,
16
+ hydraflow/executor/conf.py,sha256=2dv6_PlsynRmia-fGZlmBEVt8GopT0f32N13qY7tYnM,402
17
+ hydraflow/executor/io.py,sha256=yZMcBVmAbPZZ82cAXhgiJfj9p8WvHmzOCMBg_vtEVek,1509
18
+ hydraflow/executor/job.py,sha256=IL7ek0Vwa3Bl_gANq0wCbldNCUclo8YBckeEeO6W6xg,4852
19
+ hydraflow/executor/parser.py,sha256=llbv6BLPyO5UlExeRibkYI0vaxZbqS4q3uPZ1NnkIDM,11891
20
+ hydraflow-0.12.0.dist-info/METADATA,sha256=-CLwckmFxrCb9GEyhoz1ZaSaChVQU5_pQ0zkCCqfUMw,4549
21
+ hydraflow-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ hydraflow-0.12.0.dist-info/entry_points.txt,sha256=XI0khPbpCIUo9UPqkNEpgh-kqK3Jy8T7L2VCWOdkbSM,48
23
+ hydraflow-0.12.0.dist-info/licenses/LICENSE,sha256=IGdDrBPqz1O0v_UwCW-NJlbX9Hy9b3uJ11t28y2srmY,1062
24
+ hydraflow-0.12.0.dist-info/RECORD,,