hydraflow 0.6.2__tar.gz → 0.7.2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. {hydraflow-0.6.2 → hydraflow-0.7.2}/PKG-INFO +1 -1
  2. {hydraflow-0.6.2 → hydraflow-0.7.2}/pyproject.toml +2 -1
  3. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/__init__.py +7 -5
  4. hydraflow-0.7.2/src/hydraflow/main.py +55 -0
  5. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/run_collection.py +18 -102
  6. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/rerun.py +1 -1
  7. hydraflow-0.7.2/tests/main/force_new_run.py +26 -0
  8. hydraflow-0.7.2/tests/main/restart.py +26 -0
  9. hydraflow-0.7.2/tests/main/skip.py +26 -0
  10. hydraflow-0.7.2/tests/main/test_force_new_run.py +33 -0
  11. hydraflow-0.7.2/tests/main/test_restart.py +24 -0
  12. hydraflow-0.6.2/tests/context/test_preemption.py → hydraflow-0.7.2/tests/main/test_skip.py +22 -5
  13. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/test_collection.py +0 -38
  14. hydraflow-0.7.2/tests/utils/__init__.py +0 -0
  15. hydraflow-0.6.2/tests/context/preemption.py +0 -48
  16. {hydraflow-0.6.2 → hydraflow-0.7.2}/.devcontainer/devcontainer.json +0 -0
  17. {hydraflow-0.6.2 → hydraflow-0.7.2}/.devcontainer/postCreate.sh +0 -0
  18. {hydraflow-0.6.2 → hydraflow-0.7.2}/.devcontainer/starship.toml +0 -0
  19. {hydraflow-0.6.2 → hydraflow-0.7.2}/.gitattributes +0 -0
  20. {hydraflow-0.6.2 → hydraflow-0.7.2}/.gitignore +0 -0
  21. {hydraflow-0.6.2 → hydraflow-0.7.2}/LICENSE +0 -0
  22. {hydraflow-0.6.2 → hydraflow-0.7.2}/README.md +0 -0
  23. {hydraflow-0.6.2 → hydraflow-0.7.2}/apps/quickstart.py +0 -0
  24. {hydraflow-0.6.2 → hydraflow-0.7.2}/mkdocs.yml +0 -0
  25. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/config.py +0 -0
  26. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/context.py +0 -0
  27. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/mlflow.py +0 -0
  28. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/param.py +0 -0
  29. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/py.typed +0 -0
  30. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/run_data.py +0 -0
  31. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/run_info.py +0 -0
  32. {hydraflow-0.6.2 → hydraflow-0.7.2}/src/hydraflow/utils.py +0 -0
  33. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/__init__.py +0 -0
  34. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/config/__init__.py +0 -0
  35. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/config/overrides.py +0 -0
  36. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/config/test_config.py +0 -0
  37. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/config/test_overrides.py +0 -0
  38. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/config/test_params.py +0 -0
  39. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/conftest.py +0 -0
  40. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/__init__.py +0 -0
  41. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/chdir.py +0 -0
  42. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/context.py +0 -0
  43. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/logging.py +0 -0
  44. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/test_chdir.py +0 -0
  45. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/test_context.py +0 -0
  46. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/test_logging.py +0 -0
  47. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/context/test_rerun.py +0 -0
  48. {hydraflow-0.6.2/tests/param → hydraflow-0.7.2/tests/main}/__init__.py +0 -0
  49. {hydraflow-0.6.2/tests/run → hydraflow-0.7.2/tests/param}/__init__.py +0 -0
  50. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/param/params.py +0 -0
  51. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/param/test_param.py +0 -0
  52. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/param/test_params.py +0 -0
  53. {hydraflow-0.6.2/tests/utils → hydraflow-0.7.2/tests/run}/__init__.py +0 -0
  54. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/filter.py +0 -0
  55. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/run.py +0 -0
  56. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/test_data.py +0 -0
  57. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/test_filter.py +0 -0
  58. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/test_info.py +0 -0
  59. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/run/test_run.py +0 -0
  60. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/test_mlflow.py +0 -0
  61. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/utils/test_run.py +0 -0
  62. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/utils/test_utils.py +0 -0
  63. {hydraflow-0.6.2 → hydraflow-0.7.2}/tests/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.6.2
3
+ Version: 0.7.2
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hydraflow"
7
- version = "0.6.2"
7
+ version = "0.7.2"
8
8
  description = "Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -97,3 +97,4 @@ ignore = [
97
97
  "SLF",
98
98
  ]
99
99
  "apps/*.py" = ["D", "G", "INP"]
100
+ "src/hydraflow/main.py" = ["ANN201", "D401", "PLR0913"]
@@ -1,10 +1,11 @@
1
1
  """Integrate Hydra and MLflow to manage and track machine learning experiments."""
2
2
 
3
- from .config import select_config, select_overrides
4
- from .context import chdir_artifact, log_run, start_run
5
- from .mlflow import list_runs, search_runs, set_experiment
6
- from .run_collection import RunCollection
7
- from .utils import (
3
+ from hydraflow.config import select_config, select_overrides
4
+ from hydraflow.context import chdir_artifact, log_run, start_run
5
+ from hydraflow.main import main
6
+ from hydraflow.mlflow import list_runs, search_runs, set_experiment
7
+ from hydraflow.run_collection import RunCollection
8
+ from hydraflow.utils import (
8
9
  get_artifact_dir,
9
10
  get_artifact_path,
10
11
  get_hydra_output_dir,
@@ -25,6 +26,7 @@ __all__ = [
25
26
  "load_config",
26
27
  "load_overrides",
27
28
  "log_run",
29
+ "main",
28
30
  "remove_run",
29
31
  "search_runs",
30
32
  "select_config",
@@ -0,0 +1,55 @@
1
+ """main decorator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from functools import wraps
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ import hydra
9
+ from hydra.core.config_store import ConfigStore
10
+ from mlflow.entities import RunStatus
11
+
12
+ import hydraflow
13
+
14
+ if TYPE_CHECKING:
15
+ from collections.abc import Callable
16
+
17
+ from mlflow.entities import Run
18
+
19
+ FINISHED = RunStatus.to_string(RunStatus.FINISHED)
20
+
21
+
22
+ def main(
23
+ node: Any,
24
+ config_name: str = "config",
25
+ *,
26
+ chdir: bool = True,
27
+ skip_finished: bool = True,
28
+ force_new_run: bool = False,
29
+ override: bool = True,
30
+ ):
31
+ """Main decorator."""
32
+
33
+ def decorator(app: Callable[[Run, Any], None]) -> Callable[[], None]:
34
+ ConfigStore.instance().store(name=config_name, node=node)
35
+
36
+ @wraps(app)
37
+ @hydra.main(version_base=None, config_name=config_name)
38
+ def inner_app(cfg: object) -> None:
39
+ hydraflow.set_experiment()
40
+
41
+ if force_new_run:
42
+ run = None
43
+ else:
44
+ rc = hydraflow.search_runs()
45
+ run = rc.try_get(cfg, override=override)
46
+
47
+ if skip_finished and run and run.info.status == FINISHED:
48
+ return
49
+
50
+ with hydraflow.start_run(cfg, run=run, chdir=chdir) as run:
51
+ app(run, cfg)
52
+
53
+ return inner_app
54
+
55
+ return decorator
@@ -286,105 +286,11 @@ class RunCollection:
286
286
  ),
287
287
  )
288
288
 
289
- def find(self, config: object | None = None, **kwargs) -> Run:
290
- """Find the first `Run` instance based on the provided configuration.
291
-
292
- This method filters the runs in the collection according to the
293
- specified configuration object and returns the first run that matches
294
- the provided parameters. If no run matches the criteria, a `ValueError`
295
- is raised.
296
-
297
- Args:
298
- config (object | None): The configuration object to identify the run.
299
- **kwargs: Additional key-value pairs to filter the runs.
300
-
301
- Returns:
302
- The first `Run` instance that matches the provided configuration.
303
-
304
- Raises:
305
- ValueError: If no run matches the criteria.
306
-
307
- See Also:
308
- `filter`: Perform the actual filtering logic.
309
-
310
- """
311
- try:
312
- return self.filter(config, **kwargs).first()
313
- except ValueError:
314
- raise ValueError("No run matches the provided configuration.")
315
-
316
- def try_find(self, config: object | None = None, **kwargs) -> Run | None:
317
- """Try to find the first `Run` instance based on the provided configuration.
318
-
319
- This method filters the runs in the collection according to the
320
- specified configuration object and returns the first run that matches
321
- the provided parameters. If no run matches the criteria, None is
322
- returned.
323
-
324
- Args:
325
- config (object | None): The configuration object to identify the run.
326
- **kwargs: Additional key-value pairs to filter the runs.
327
-
328
- Returns:
329
- The first `Run` instance that matches the provided configuration, or
330
- None if no runs match the criteria.
331
-
332
- See Also:
333
- `filter`: Perform the actual filtering logic.
334
-
335
- """
336
- return self.filter(config, **kwargs).try_first()
337
-
338
- def find_last(self, config: object | None = None, **kwargs) -> Run:
339
- """Find the last `Run` instance based on the provided configuration.
340
-
341
- This method filters the runs in the collection according to the
342
- specified configuration object and returns the last run that matches
343
- the provided parameters. If no run matches the criteria, a `ValueError`
344
- is raised.
345
-
346
- Args:
347
- config (object | None): The configuration object to identify the run.
348
- **kwargs: Additional key-value pairs to filter the runs.
349
-
350
- Returns:
351
- The last `Run` instance that matches the provided configuration.
352
-
353
- Raises:
354
- ValueError: If no run matches the criteria.
355
-
356
- See Also:
357
- `filter`: Perform the actual filtering logic.
358
-
359
- """
360
- try:
361
- return self.filter(config, **kwargs).last()
362
- except ValueError:
363
- raise ValueError("No run matches the provided configuration.")
364
-
365
- def try_find_last(self, config: object | None = None, **kwargs) -> Run | None:
366
- """Try to find the last `Run` instance based on the provided configuration.
367
-
368
- This method filters the runs in the collection according to the
369
- specified configuration object and returns the last run that matches
370
- the provided parameters. If no run matches the criteria, None is
371
- returned.
372
-
373
- Args:
374
- config (object | None): The configuration object to identify the run.
375
- **kwargs: Additional key-value pairs to filter the runs.
376
-
377
- Returns:
378
- The last `Run` instance that matches the provided configuration, or
379
- None if no runs match the criteria.
380
-
381
- See Also:
382
- `filter`: Perform the actual filtering logic.
383
-
384
- """
385
- return self.filter(config, **kwargs).try_last()
386
-
387
- def get(self, config: object | None = None, **kwargs) -> Run:
289
+ def get(
290
+ self,
291
+ config: object | Callable[[Run], bool] | None = None,
292
+ **kwargs,
293
+ ) -> Run:
388
294
  """Retrieve a specific `Run` instance based on the provided configuration.
389
295
 
390
296
  This method filters the runs in the collection according to the
@@ -393,7 +299,10 @@ class RunCollection:
393
299
  one run matches the criteria, a `ValueError` is raised.
394
300
 
395
301
  Args:
396
- config (object | None): The configuration object to identify the run.
302
+ config (object | Callable[[Run], bool] | None): The configuration object
303
+ to identify the run. This can be any object that provides key-value
304
+ pairs through the `iter_params` function, or a callable that
305
+ takes a `Run` object and returns a boolean value.
397
306
  **kwargs: Additional key-value pairs to filter the runs.
398
307
 
399
308
  Returns:
@@ -413,7 +322,11 @@ class RunCollection:
413
322
  msg = "The filtered collection does not contain exactly one run."
414
323
  raise ValueError(msg)
415
324
 
416
- def try_get(self, config: object | None = None, **kwargs) -> Run | None:
325
+ def try_get(
326
+ self,
327
+ config: object | Callable[[Run], bool] | None = None,
328
+ **kwargs,
329
+ ) -> Run | None:
417
330
  """Try to get a specific `Run` instance based on the provided configuration.
418
331
 
419
332
  This method filters the runs in the collection according to the
@@ -422,7 +335,10 @@ class RunCollection:
422
335
  If more than one run matches the criteria, a `ValueError` is raised.
423
336
 
424
337
  Args:
425
- config (object | None): The configuration object to identify the run.
338
+ config (object | Callable[[Run], bool] | None): The configuration object
339
+ to identify the run. This can be any object that provides key-value
340
+ pairs through the `iter_params` function, or a callable that
341
+ takes a `Run` object and returns a boolean value.
426
342
  **kwargs: Additional key-value pairs to filter the runs.
427
343
 
428
344
  Returns:
@@ -24,7 +24,7 @@ ConfigStore.instance().store(name="config", node=Config)
24
24
  def app(cfg: Config):
25
25
  hydraflow.set_experiment()
26
26
 
27
- run = hydraflow.list_runs().try_find(cfg, override=True)
27
+ run = hydraflow.list_runs().try_get(cfg, override=True)
28
28
 
29
29
  with hydraflow.start_run(cfg, run=run) as run:
30
30
  log(hydraflow.get_artifact_dir(run))
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ import hydraflow
8
+
9
+ if TYPE_CHECKING:
10
+ from mlflow.entities import Run
11
+
12
+
13
+ @dataclass
14
+ class Config:
15
+ count: int = 0
16
+
17
+
18
+ @hydraflow.main(Config, force_new_run=True)
19
+ def app(run: Run, cfg: Config):
20
+ file = Path("a.txt")
21
+ text = file.read_text() if file.exists() else ""
22
+ file.write_text(text + f"{cfg.count}")
23
+
24
+
25
+ if __name__ == "__main__":
26
+ app()
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ import hydraflow
8
+
9
+ if TYPE_CHECKING:
10
+ from mlflow.entities import Run
11
+
12
+
13
+ @dataclass
14
+ class Config:
15
+ count: int = 0
16
+
17
+
18
+ @hydraflow.main(Config, skip_finished=False)
19
+ def app(run: Run, cfg: Config):
20
+ file = Path("a.txt")
21
+ text = file.read_text() if file.exists() else ""
22
+ file.write_text(text + f"{cfg.count}")
23
+
24
+
25
+ if __name__ == "__main__":
26
+ app()
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ import hydraflow
8
+
9
+ if TYPE_CHECKING:
10
+ from mlflow.entities import Run
11
+
12
+
13
+ @dataclass
14
+ class Config:
15
+ count: int = 0
16
+
17
+
18
+ @hydraflow.main(Config)
19
+ def app(run: Run, cfg: Config):
20
+ file = Path("a.txt")
21
+ text = file.read_text() if file.exists() else ""
22
+ file.write_text(text + f"{cfg.count} {run.info.run_id}\n")
23
+
24
+
25
+ if __name__ == "__main__":
26
+ app()
@@ -0,0 +1,33 @@
1
+ import pytest
2
+ from mlflow.entities import Run
3
+
4
+ from hydraflow.run_collection import RunCollection
5
+
6
+ pytestmark = pytest.mark.xdist_group(name="group6")
7
+
8
+
9
+ @pytest.fixture(scope="module")
10
+ def rc(collect):
11
+ for _ in range(3):
12
+ rc = collect("main/force_new_run.py", ["count=3"])
13
+ return rc
14
+
15
+
16
+ def test_rc_len(rc: RunCollection):
17
+ assert len(rc) == 3
18
+
19
+
20
+ def test_rc_filter(rc: RunCollection):
21
+ assert len(rc.filter(count=3)) == 3
22
+
23
+
24
+ @pytest.fixture(scope="module", params=range(3))
25
+ def run(rc: RunCollection, request: pytest.FixtureRequest):
26
+ return rc[request.param]
27
+
28
+
29
+ def test_count(run: Run):
30
+ from hydraflow.utils import get_artifact_path
31
+
32
+ path = get_artifact_path(run, "a.txt")
33
+ assert path.read_text() == "3"
@@ -0,0 +1,24 @@
1
+ import pytest
2
+
3
+ from hydraflow.run_collection import RunCollection
4
+
5
+ pytestmark = pytest.mark.xdist_group(name="group7")
6
+
7
+
8
+ @pytest.fixture(scope="module")
9
+ def rc(collect):
10
+ for _ in range(3):
11
+ rc = collect("main/restart.py", ["count=3"])
12
+ return rc
13
+
14
+
15
+ def test_rc_len(rc: RunCollection):
16
+ assert len(rc) == 1
17
+
18
+
19
+ def test_count(rc: RunCollection):
20
+ from hydraflow.utils import get_artifact_path
21
+
22
+ run = rc.get(count=3)
23
+ path = get_artifact_path(run, "a.txt")
24
+ assert path.read_text() == "333"
@@ -12,7 +12,7 @@ def rc(collect):
12
12
  client = MlflowClient()
13
13
  running = RunStatus.to_string(RunStatus.RUNNING)
14
14
 
15
- filename = "context/preemption.py"
15
+ filename = "main/skip.py"
16
16
  args = ["-m", "count=1,2,3"]
17
17
 
18
18
  rc = collect(filename, args)
@@ -32,10 +32,27 @@ def run(rc: RunCollection, request: pytest.FixtureRequest):
32
32
  return rc.get(count=request.param)
33
33
 
34
34
 
35
- def test_run_count(run: Run):
35
+ @pytest.fixture(scope="module")
36
+ def count(run: Run):
37
+ return int(run.data.params["count"])
38
+
39
+
40
+ @pytest.fixture(scope="module")
41
+ def text(run: Run):
36
42
  from hydraflow.utils import get_artifact_path
37
43
 
38
- count = int(run.data.params["count"])
39
44
  path = get_artifact_path(run, "a.txt")
40
- text = path.read_text()
41
- assert len(text) == count
45
+ return path.read_text()
46
+
47
+
48
+ def test_count(text: str, count: int):
49
+ assert len(text.splitlines()) == count
50
+
51
+
52
+ def test_config(text: str, count: int):
53
+ assert int(text.split(" ", maxsplit=1)[0]) == count
54
+
55
+
56
+ def test_run(text: str, run: Run):
57
+ line = text.splitlines()[-1]
58
+ assert line.split(" ", maxsplit=1)[1] == run.info.run_id
@@ -332,44 +332,6 @@ def test_get_param_dict_drop_const(rc: RunCollection):
332
332
  assert "r" in params
333
333
 
334
334
 
335
- def test_find_dict(rc: RunCollection):
336
- run = rc.find({"r": 0})
337
- assert run.data.params["p"] == "0"
338
-
339
-
340
- def test_find_kwarg(rc: RunCollection):
341
- run = rc.find(r=2)
342
- assert run.data.params["p"] == "2"
343
-
344
-
345
- def test_find_none(rc: RunCollection):
346
- with pytest.raises(ValueError):
347
- rc.find({"r": 10})
348
-
349
-
350
- def test_try_find_none(rc: RunCollection):
351
- assert rc.try_find({"r": 10}) is None
352
-
353
-
354
- def test_find_last_dict(rc: RunCollection):
355
- run = rc.find_last({"r": 0})
356
- assert run.data.params["p"] == "3"
357
-
358
-
359
- def test_find_last_kwarg(rc: RunCollection):
360
- run = rc.find_last(r=2)
361
- assert run.data.params["p"] == "5"
362
-
363
-
364
- def test_find_last_none(rc: RunCollection):
365
- with pytest.raises(ValueError):
366
- rc.find_last({"p": 10})
367
-
368
-
369
- def test_try_find_last_none(rc: RunCollection):
370
- assert rc.try_find_last({"p": 10}) is None
371
-
372
-
373
335
  @pytest.mark.parametrize("n_jobs", [0, 1, 2])
374
336
  def test_list_runs(rc: RunCollection, n_jobs: int):
375
337
  assert len(list_runs(n_jobs=n_jobs)) == 6
File without changes
@@ -1,48 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
- from typing import TYPE_CHECKING
5
-
6
- import hydra
7
- from hydra.core.config_store import ConfigStore
8
-
9
- import hydraflow
10
-
11
- if TYPE_CHECKING:
12
- from pathlib import Path
13
-
14
-
15
- @dataclass
16
- class Config:
17
- count: int = 0
18
-
19
-
20
- ConfigStore.instance().store(name="config", node=Config)
21
-
22
-
23
- @hydra.main(version_base=None, config_name="config")
24
- def app(cfg: Config):
25
- hydraflow.set_experiment()
26
-
27
- rc = hydraflow.list_runs()
28
-
29
- if rc.filter(cfg, status="finished", override=True):
30
- return
31
-
32
- if run := rc.try_find(cfg, override=True):
33
- run_id = run.info.run_id
34
- else:
35
- run_id = None
36
-
37
- with hydraflow.start_run(cfg, run_id=run_id) as run:
38
- log(hydraflow.get_artifact_dir(run))
39
-
40
-
41
- def log(path: Path):
42
- file = path / "a.txt"
43
- text = file.read_text() if file.exists() else ""
44
- file.write_text(text + "a")
45
-
46
-
47
- if __name__ == "__main__":
48
- app()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes