hydraflow 0.7.0__tar.gz → 0.7.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. {hydraflow-0.7.0 → hydraflow-0.7.3}/PKG-INFO +1 -1
  2. {hydraflow-0.7.0 → hydraflow-0.7.3}/pyproject.toml +2 -1
  3. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/__init__.py +7 -5
  4. hydraflow-0.7.3/src/hydraflow/main.py +54 -0
  5. hydraflow-0.7.3/tests/main/base.py +27 -0
  6. hydraflow-0.7.3/tests/main/force_new_run.py +26 -0
  7. hydraflow-0.7.3/tests/main/restart.py +26 -0
  8. hydraflow-0.7.3/tests/main/skip.py +26 -0
  9. hydraflow-0.7.3/tests/main/test_base.py +58 -0
  10. hydraflow-0.7.3/tests/main/test_force_new_run.py +33 -0
  11. hydraflow-0.7.3/tests/main/test_restart.py +24 -0
  12. hydraflow-0.7.0/tests/context/test_preemption.py → hydraflow-0.7.3/tests/main/test_skip.py +22 -5
  13. hydraflow-0.7.3/tests/utils/__init__.py +0 -0
  14. hydraflow-0.7.0/tests/context/preemption.py +0 -43
  15. {hydraflow-0.7.0 → hydraflow-0.7.3}/.devcontainer/devcontainer.json +0 -0
  16. {hydraflow-0.7.0 → hydraflow-0.7.3}/.devcontainer/postCreate.sh +0 -0
  17. {hydraflow-0.7.0 → hydraflow-0.7.3}/.devcontainer/starship.toml +0 -0
  18. {hydraflow-0.7.0 → hydraflow-0.7.3}/.gitattributes +0 -0
  19. {hydraflow-0.7.0 → hydraflow-0.7.3}/.gitignore +0 -0
  20. {hydraflow-0.7.0 → hydraflow-0.7.3}/LICENSE +0 -0
  21. {hydraflow-0.7.0 → hydraflow-0.7.3}/README.md +0 -0
  22. {hydraflow-0.7.0 → hydraflow-0.7.3}/apps/quickstart.py +0 -0
  23. {hydraflow-0.7.0 → hydraflow-0.7.3}/mkdocs.yml +0 -0
  24. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/config.py +0 -0
  25. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/context.py +0 -0
  26. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/mlflow.py +0 -0
  27. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/param.py +0 -0
  28. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/py.typed +0 -0
  29. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/run_collection.py +0 -0
  30. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/run_data.py +0 -0
  31. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/run_info.py +0 -0
  32. {hydraflow-0.7.0 → hydraflow-0.7.3}/src/hydraflow/utils.py +0 -0
  33. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/__init__.py +0 -0
  34. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/config/__init__.py +0 -0
  35. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/config/overrides.py +0 -0
  36. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/config/test_config.py +0 -0
  37. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/config/test_overrides.py +0 -0
  38. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/config/test_params.py +0 -0
  39. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/conftest.py +0 -0
  40. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/__init__.py +0 -0
  41. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/chdir.py +0 -0
  42. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/context.py +0 -0
  43. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/logging.py +0 -0
  44. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/rerun.py +0 -0
  45. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/test_chdir.py +0 -0
  46. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/test_context.py +0 -0
  47. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/test_logging.py +0 -0
  48. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/context/test_rerun.py +0 -0
  49. {hydraflow-0.7.0/tests/param → hydraflow-0.7.3/tests/main}/__init__.py +0 -0
  50. {hydraflow-0.7.0/tests/run → hydraflow-0.7.3/tests/param}/__init__.py +0 -0
  51. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/param/params.py +0 -0
  52. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/param/test_param.py +0 -0
  53. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/param/test_params.py +0 -0
  54. {hydraflow-0.7.0/tests/utils → hydraflow-0.7.3/tests/run}/__init__.py +0 -0
  55. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/filter.py +0 -0
  56. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/run.py +0 -0
  57. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/test_collection.py +0 -0
  58. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/test_data.py +0 -0
  59. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/test_filter.py +0 -0
  60. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/test_info.py +0 -0
  61. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/run/test_run.py +0 -0
  62. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/test_mlflow.py +0 -0
  63. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/utils/test_run.py +0 -0
  64. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/utils/test_utils.py +0 -0
  65. {hydraflow-0.7.0 → hydraflow-0.7.3}/tests/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.7.0
3
+ Version: 0.7.3
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.7.0"
7
+ version = "0.7.3"
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,54 @@
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 = False,
27
+ force_new_run: bool = False,
28
+ skip_finished: bool = True,
29
+ ):
30
+ """Main decorator."""
31
+
32
+ def decorator(app: Callable[[Run, Any], None]) -> Callable[[], None]:
33
+ ConfigStore.instance().store(name=config_name, node=node)
34
+
35
+ @wraps(app)
36
+ @hydra.main(version_base=None, config_name=config_name)
37
+ def inner_app(cfg: object) -> None:
38
+ hydraflow.set_experiment()
39
+
40
+ if force_new_run:
41
+ run = None
42
+ else:
43
+ rc = hydraflow.search_runs()
44
+ run = rc.try_get(cfg, override=True)
45
+
46
+ if skip_finished and run and run.info.status == FINISHED:
47
+ return
48
+
49
+ with hydraflow.start_run(cfg, run=run, chdir=chdir) as run:
50
+ app(run, cfg)
51
+
52
+ return inner_app
53
+
54
+ return decorator
@@ -0,0 +1,27 @@
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
+ path = hydraflow.get_artifact_dir() / "a.txt"
21
+ path.write_text(f"{run.info.run_id},{cfg.count}")
22
+ path = hydraflow.get_artifact_dir() / "b.txt"
23
+ path.write_text(f"{Path.cwd().absolute().as_posix()}")
24
+
25
+
26
+ if __name__ == "__main__":
27
+ 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, chdir=True, 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, chdir=True, 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, chdir=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} {run.info.run_id}\n")
23
+
24
+
25
+ if __name__ == "__main__":
26
+ app()
@@ -0,0 +1,58 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+ from mlflow.entities import Run
5
+
6
+ from hydraflow.run_collection import RunCollection
7
+
8
+ pytestmark = pytest.mark.xdist_group(name="group5")
9
+
10
+
11
+ @pytest.fixture(scope="module")
12
+ def rc(collect):
13
+ filename = "main/base.py"
14
+ args = ["-m", "count=1,2,3"]
15
+
16
+ return collect(filename, args)
17
+
18
+
19
+ def test_rc_len(rc: RunCollection):
20
+ assert len(rc) == 3
21
+
22
+
23
+ @pytest.fixture(scope="module", params=[1, 2, 3])
24
+ def run(rc: RunCollection, request: pytest.FixtureRequest):
25
+ return rc.get(count=request.param)
26
+
27
+
28
+ @pytest.fixture(scope="module")
29
+ def count(run: Run):
30
+ return int(run.data.params["count"])
31
+
32
+
33
+ @pytest.fixture(scope="module")
34
+ def text(run: Run):
35
+ from hydraflow.utils import get_artifact_path
36
+
37
+ path = get_artifact_path(run, "a.txt")
38
+ return path.read_text()
39
+
40
+
41
+ def test_run_id(run: Run, text: str):
42
+ assert text.split(",")[0] == run.info.run_id
43
+
44
+
45
+ def test_count(text: str, count: int):
46
+ assert text.split(",")[1] == str(count)
47
+
48
+
49
+ @pytest.fixture(scope="module")
50
+ def cwd(run: Run):
51
+ from hydraflow.utils import get_artifact_path
52
+
53
+ path = get_artifact_path(run, "b.txt")
54
+ return Path(path.read_text())
55
+
56
+
57
+ def test_cwd(cwd: Path, experiment_name: str):
58
+ assert cwd.name == experiment_name
@@ -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
File without changes
@@ -1,43 +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().filter(cfg, override=True)
28
-
29
- if rc.filter(status="finished"):
30
- return
31
-
32
- with hydraflow.start_run(cfg, run=rc.try_one()) as run:
33
- log(hydraflow.get_artifact_dir(run))
34
-
35
-
36
- def log(path: Path):
37
- file = path / "a.txt"
38
- text = file.read_text() if file.exists() else ""
39
- file.write_text(text + "a")
40
-
41
-
42
- if __name__ == "__main__":
43
- 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