hydraflow 0.2.1__tar.gz → 0.2.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. {hydraflow-0.2.1 → hydraflow-0.2.3}/.devcontainer/devcontainer.json +6 -1
  2. {hydraflow-0.2.1 → hydraflow-0.2.3}/.gitignore +3 -2
  3. {hydraflow-0.2.1 → hydraflow-0.2.3}/PKG-INFO +17 -6
  4. {hydraflow-0.2.1 → hydraflow-0.2.3}/README.md +14 -5
  5. {hydraflow-0.2.1 → hydraflow-0.2.3}/pyproject.toml +16 -9
  6. {hydraflow-0.2.1 → hydraflow-0.2.3}/src/hydraflow/__init__.py +6 -2
  7. hydraflow-0.2.3/src/hydraflow/asyncio.py +199 -0
  8. {hydraflow-0.2.1 → hydraflow-0.2.3}/src/hydraflow/config.py +3 -0
  9. hydraflow-0.2.3/src/hydraflow/runs.py +849 -0
  10. hydraflow-0.2.3/tests/scripts/watch.py +9 -0
  11. hydraflow-0.2.3/tests/test_asyncio.py +159 -0
  12. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_config.py +6 -0
  13. hydraflow-0.2.3/tests/test_runs.py +403 -0
  14. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_watch.py +5 -8
  15. hydraflow-0.2.1/src/hydraflow/runs.py +0 -422
  16. hydraflow-0.2.1/tests/scripts/watch.py +0 -23
  17. hydraflow-0.2.1/tests/test_runs.py +0 -277
  18. {hydraflow-0.2.1 → hydraflow-0.2.3}/.devcontainer/postCreate.sh +0 -0
  19. {hydraflow-0.2.1 → hydraflow-0.2.3}/.devcontainer/starship.toml +0 -0
  20. {hydraflow-0.2.1 → hydraflow-0.2.3}/.gitattributes +0 -0
  21. {hydraflow-0.2.1 → hydraflow-0.2.3}/LICENSE +0 -0
  22. {hydraflow-0.2.1 → hydraflow-0.2.3}/src/hydraflow/context.py +0 -0
  23. {hydraflow-0.2.1 → hydraflow-0.2.3}/src/hydraflow/mlflow.py +0 -0
  24. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/scripts/__init__.py +0 -0
  25. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/scripts/log_run.py +0 -0
  26. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_context.py +0 -0
  27. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_log_run.py +0 -0
  28. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_mlflow.py +0 -0
  29. {hydraflow-0.2.1 → hydraflow-0.2.3}/tests/test_version.py +0 -0
@@ -7,7 +7,12 @@
7
7
  },
8
8
  "customizations": {
9
9
  "vscode": {
10
- "extensions": ["charliermarsh.ruff"]
10
+ "extensions": [
11
+ "charliermarsh.ruff",
12
+ "henriiik.vscode-sort",
13
+ "ms-python.python",
14
+ "ms-python.vscode-pylance"
15
+ ]
11
16
  }
12
17
  },
13
18
  "postCreateCommand": ".devcontainer/postCreate.sh"
@@ -1,5 +1,6 @@
1
1
  .coverage
2
+ .env
2
3
  .venv/
3
4
  __pycache__/
4
- lcov.info
5
- dist/
5
+ dist/
6
+ lcov.info
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hydraflow
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments.
5
5
  Project-URL: Documentation, https://github.com/daizutabi/hydraflow
6
6
  Project-URL: Source, https://github.com/daizutabi/hydraflow
@@ -20,7 +20,9 @@ Requires-Dist: hydra-core>1.3
20
20
  Requires-Dist: mlflow>2.15
21
21
  Requires-Dist: setuptools
22
22
  Requires-Dist: watchdog
23
+ Requires-Dist: watchfiles
23
24
  Provides-Extra: dev
25
+ Requires-Dist: pytest-asyncio; extra == 'dev'
24
26
  Requires-Dist: pytest-clarity; extra == 'dev'
25
27
  Requires-Dist: pytest-cov; extra == 'dev'
26
28
  Requires-Dist: pytest-randomly; extra == 'dev'
@@ -46,14 +48,23 @@ Description-Content-Type: text/markdown
46
48
 
47
49
  ## Overview
48
50
 
49
- Hydraflow is a powerful library designed to seamlessly integrate [Hydra](https://hydra.cc/) and [MLflow](https://mlflow.org/), making it easier to manage and track machine learning experiments. By combining the flexibility of Hydra's configuration management with the robust experiment tracking capabilities of MLflow, Hydraflow provides a comprehensive solution for managing complex machine learning workflows.
51
+ Hydraflow is a powerful library designed to seamlessly integrate
52
+ [Hydra](https://hydra.cc/) and [MLflow](https://mlflow.org/), making it easier to
53
+ manage and track machine learning experiments. By combining the flexibility of
54
+ Hydra's configuration management with the robust experiment tracking capabilities
55
+ of MLflow, Hydraflow provides a comprehensive solution for managing complex
56
+ machine learning workflows.
50
57
 
51
58
  ## Key Features
52
59
 
53
- - **Configuration Management**: Utilize Hydra's advanced configuration management to handle complex parameter sweeps and experiment setups.
54
- - **Experiment Tracking**: Leverage MLflow's tracking capabilities to log parameters, metrics, and artifacts for each run.
55
- - **Artifact Management**: Automatically log and manage artifacts, such as model checkpoints and configuration files, with MLflow.
56
- - **Seamless Integration**: Easily integrate Hydra and MLflow in your machine learning projects with minimal setup.
60
+ - **Configuration Management**: Utilize Hydra's advanced configuration management
61
+ to handle complex parameter sweeps and experiment setups.
62
+ - **Experiment Tracking**: Leverage MLflow's tracking capabilities to log parameters,
63
+ metrics, and artifacts for each run.
64
+ - **Artifact Management**: Automatically log and manage artifacts, such as model
65
+ checkpoints and configuration files, with MLflow.
66
+ - **Seamless Integration**: Easily integrate Hydra and MLflow in your machine learning
67
+ projects with minimal setup.
57
68
 
58
69
  ## Installation
59
70
 
@@ -17,14 +17,23 @@
17
17
 
18
18
  ## Overview
19
19
 
20
- Hydraflow is a powerful library designed to seamlessly integrate [Hydra](https://hydra.cc/) and [MLflow](https://mlflow.org/), making it easier to manage and track machine learning experiments. By combining the flexibility of Hydra's configuration management with the robust experiment tracking capabilities of MLflow, Hydraflow provides a comprehensive solution for managing complex machine learning workflows.
20
+ Hydraflow is a powerful library designed to seamlessly integrate
21
+ [Hydra](https://hydra.cc/) and [MLflow](https://mlflow.org/), making it easier to
22
+ manage and track machine learning experiments. By combining the flexibility of
23
+ Hydra's configuration management with the robust experiment tracking capabilities
24
+ of MLflow, Hydraflow provides a comprehensive solution for managing complex
25
+ machine learning workflows.
21
26
 
22
27
  ## Key Features
23
28
 
24
- - **Configuration Management**: Utilize Hydra's advanced configuration management to handle complex parameter sweeps and experiment setups.
25
- - **Experiment Tracking**: Leverage MLflow's tracking capabilities to log parameters, metrics, and artifacts for each run.
26
- - **Artifact Management**: Automatically log and manage artifacts, such as model checkpoints and configuration files, with MLflow.
27
- - **Seamless Integration**: Easily integrate Hydra and MLflow in your machine learning projects with minimal setup.
29
+ - **Configuration Management**: Utilize Hydra's advanced configuration management
30
+ to handle complex parameter sweeps and experiment setups.
31
+ - **Experiment Tracking**: Leverage MLflow's tracking capabilities to log parameters,
32
+ metrics, and artifacts for each run.
33
+ - **Artifact Management**: Automatically log and manage artifacts, such as model
34
+ checkpoints and configuration files, with MLflow.
35
+ - **Seamless Integration**: Easily integrate Hydra and MLflow in your machine learning
36
+ projects with minimal setup.
28
37
 
29
38
  ## Installation
30
39
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hydraflow"
7
- version = "0.2.1"
7
+ version = "0.2.3"
8
8
  description = "Hydraflow integrates Hydra and MLflow to manage and track machine learning experiments."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -19,10 +19,22 @@ classifiers = [
19
19
  "Topic :: Software Development :: Documentation",
20
20
  ]
21
21
  requires-python = ">=3.10"
22
- dependencies = ["hydra-core>1.3", "mlflow>2.15", "setuptools", "watchdog"]
22
+ dependencies = [
23
+ "hydra-core>1.3",
24
+ "mlflow>2.15",
25
+ "setuptools",
26
+ "watchdog",
27
+ "watchfiles",
28
+ ]
23
29
 
24
30
  [project.optional-dependencies]
25
- dev = ["pytest-clarity", "pytest-cov", "pytest-randomly", "pytest-xdist"]
31
+ dev = [
32
+ "pytest-asyncio",
33
+ "pytest-clarity",
34
+ "pytest-cov",
35
+ "pytest-randomly",
36
+ "pytest-xdist",
37
+ ]
26
38
 
27
39
  [project.urls]
28
40
  Documentation = "https://github.com/daizutabi/hydraflow"
@@ -41,18 +53,13 @@ addopts = [
41
53
  "--cov=hydraflow",
42
54
  "--cov-report=lcov:lcov.info",
43
55
  ]
44
-
45
56
  doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
46
57
  filterwarnings = ['ignore:pkg_resources is deprecated:DeprecationWarning']
58
+ asyncio_default_fixture_loop_scope = "function"
47
59
 
48
60
  [tool.coverage.report]
49
61
  exclude_lines = ["no cov", "raise NotImplementedError", "if TYPE_CHECKING:"]
50
62
 
51
- [tool.hatch.envs.docs.scripts]
52
- build = "mkdocs build --clean --strict {args}"
53
- serve = "mkdocs serve --dev-addr localhost:8000 {args}"
54
- deploy = "mkdocs gh-deploy --force"
55
-
56
63
  [tool.ruff]
57
64
  line-length = 100
58
65
  target-version = "py312"
@@ -2,25 +2,29 @@ from .context import Info, chdir_artifact, log_run, watch
2
2
  from .mlflow import set_experiment
3
3
  from .runs import (
4
4
  Run,
5
- Runs,
5
+ RunCollection,
6
6
  filter_runs,
7
7
  get_param_dict,
8
8
  get_param_names,
9
9
  get_run,
10
+ list_runs,
10
11
  load_config,
12
+ search_runs,
11
13
  )
12
14
 
13
15
  __all__ = [
14
16
  "Info",
15
17
  "Run",
16
- "Runs",
18
+ "RunCollection",
17
19
  "chdir_artifact",
18
20
  "filter_runs",
19
21
  "get_param_dict",
20
22
  "get_param_names",
21
23
  "get_run",
24
+ "list_runs",
22
25
  "load_config",
23
26
  "log_run",
27
+ "search_runs",
24
28
  "set_experiment",
25
29
  "watch",
26
30
  ]
@@ -0,0 +1,199 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ from asyncio.subprocess import PIPE
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING
8
+
9
+ import watchfiles
10
+
11
+ if TYPE_CHECKING:
12
+ from asyncio.streams import StreamReader
13
+ from collections.abc import Callable
14
+
15
+ from watchfiles import Change
16
+
17
+
18
+ # Set up logging
19
+ logging.basicConfig(level=logging.INFO)
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ async def execute_command(
24
+ program: str,
25
+ *args: str,
26
+ stdout: Callable[[str], None] | None = None,
27
+ stderr: Callable[[str], None] | None = None,
28
+ stop_event: asyncio.Event,
29
+ ) -> int:
30
+ """
31
+ Runs a command asynchronously and pass the output to callback functions.
32
+
33
+ Args:
34
+ program (str): The program to run.
35
+ *args (str): Arguments for the program.
36
+ stdout (Callable[[str], None] | None): Callback for standard output.
37
+ stderr (Callable[[str], None] | None): Callback for standard error.
38
+ stop_event (asyncio.Event): Event to signal when the process is done.
39
+
40
+ Returns:
41
+ int: The return code of the process.
42
+ """
43
+ try:
44
+ process = await asyncio.create_subprocess_exec(program, *args, stdout=PIPE, stderr=PIPE)
45
+ await asyncio.gather(
46
+ process_stream(process.stdout, stdout),
47
+ process_stream(process.stderr, stderr),
48
+ )
49
+ returncode = await process.wait()
50
+
51
+ except Exception as e:
52
+ logger.error(f"Error running command: {e}")
53
+ returncode = 1
54
+
55
+ finally:
56
+ stop_event.set()
57
+
58
+ return returncode
59
+
60
+
61
+ async def process_stream(
62
+ stream: StreamReader | None,
63
+ callback: Callable[[str], None] | None,
64
+ ) -> None:
65
+ """
66
+ Reads a stream asynchronously and pass each line to a callback function.
67
+
68
+ Args:
69
+ stream (StreamReader | None): The stream to read from.
70
+ callback (Callable[[str], None] | None): The callback function to handle
71
+ each line.
72
+ """
73
+ if stream is None or callback is None:
74
+ return
75
+
76
+ while True:
77
+ line = await stream.readline()
78
+ if line:
79
+ callback(line.decode().strip())
80
+ else:
81
+ break
82
+
83
+
84
+ async def monitor_file_changes(
85
+ paths: list[str | Path],
86
+ callback: Callable[[set[tuple[Change, str]]], None],
87
+ stop_event: asyncio.Event,
88
+ **awatch_kwargs,
89
+ ) -> None:
90
+ """
91
+ Watches for file changes in specified paths and pass the changes to a
92
+ callback function.
93
+
94
+ Args:
95
+ paths (list[str | Path]): List of paths to monitor for changes.
96
+ callback (Callable[[set[tuple[Change, str]]], None]): The callback
97
+ function to handle file changes.
98
+ stop_event (asyncio.Event): Event to signal when to stop watching.
99
+ **awatch_kwargs: Additional keyword arguments to pass to watchfiles.awatch.
100
+ """
101
+ str_paths = [str(path) for path in paths]
102
+ try:
103
+ async for changes in watchfiles.awatch(*str_paths, stop_event=stop_event, **awatch_kwargs):
104
+ callback(changes)
105
+ except Exception as e:
106
+ logger.error(f"Error watching files: {e}")
107
+
108
+
109
+ async def run_and_monitor(
110
+ program: str,
111
+ *args: str,
112
+ stdout: Callable[[str], None] | None = None,
113
+ stderr: Callable[[str], None] | None = None,
114
+ watch: Callable[[set[tuple[Change, str]]], None] | None = None,
115
+ paths: list[str | Path] | None = None,
116
+ **awatch_kwargs,
117
+ ) -> int:
118
+ """
119
+ Runs a command and optionally watch for file changes concurrently.
120
+
121
+ Args:
122
+ program (str): The program to run.
123
+ *args (str): Arguments for the program.
124
+ stdout (Callable[[str], None] | None): Callback for standard output.
125
+ stderr (Callable[[str], None] | None): Callback for standard error.
126
+ watch (Callable[[set[tuple[Change, str]]], None] | None): Callback for
127
+ file changes.
128
+ paths (list[str | Path] | None): List of paths to monitor for changes.
129
+ """
130
+ stop_event = asyncio.Event()
131
+ run_task = asyncio.create_task(
132
+ execute_command(program, *args, stop_event=stop_event, stdout=stdout, stderr=stderr)
133
+ )
134
+ if watch and paths:
135
+ monitor_task = asyncio.create_task(
136
+ monitor_file_changes(paths, watch, stop_event, **awatch_kwargs)
137
+ )
138
+ else:
139
+ monitor_task = None
140
+
141
+ try:
142
+ if monitor_task:
143
+ await asyncio.gather(run_task, monitor_task)
144
+ else:
145
+ await run_task
146
+
147
+ except Exception as e:
148
+ logger.error(f"Error in run_and_monitor: {e}")
149
+ finally:
150
+ stop_event.set()
151
+ await run_task
152
+ if monitor_task:
153
+ await monitor_task
154
+
155
+ return run_task.result()
156
+
157
+
158
+ def run(
159
+ program: str,
160
+ *args: str,
161
+ stdout: Callable[[str], None] | None = None,
162
+ stderr: Callable[[str], None] | None = None,
163
+ watch: Callable[[set[tuple[Change, str]]], None] | None = None,
164
+ paths: list[str | Path] | None = None,
165
+ **awatch_kwargs,
166
+ ) -> int:
167
+ """
168
+ Run a command synchronously and optionally watch for file changes.
169
+
170
+ This function is a synchronous wrapper around the asynchronous `run_and_monitor` function.
171
+ It runs a specified command and optionally monitors specified paths for file changes,
172
+ invoking the provided callbacks for standard output, standard error, and file changes.
173
+
174
+ Args:
175
+ program (str): The program to run.
176
+ *args (str): Arguments for the program.
177
+ stdout (Callable[[str], None] | None): Callback for handling standard output lines.
178
+ stderr (Callable[[str], None] | None): Callback for handling standard error lines.
179
+ watch (Callable[[set[tuple[Change, str]]], None] | None): Callback for handling file changes.
180
+ paths (list[str | Path] | None): List of paths to monitor for file changes.
181
+ **awatch_kwargs: Additional keyword arguments to pass to `watchfiles.awatch`.
182
+
183
+ Returns:
184
+ int: The return code of the process.
185
+ """
186
+ if watch and not paths:
187
+ paths = [Path.cwd()]
188
+
189
+ return asyncio.run(
190
+ run_and_monitor(
191
+ program,
192
+ *args,
193
+ stdout=stdout,
194
+ stderr=stderr,
195
+ watch=watch,
196
+ paths=paths,
197
+ **awatch_kwargs,
198
+ )
199
+ )
@@ -30,6 +30,9 @@ def iter_params(config: object, prefix: str = "") -> Iterator[tuple[str, Any]]:
30
30
  Yields:
31
31
  Key-value pairs representing the parameters in the configuration object.
32
32
  """
33
+ if config is None:
34
+ return
35
+
33
36
  if not isinstance(config, (DictConfig, ListConfig)):
34
37
  config = OmegaConf.create(config) # type: ignore
35
38