hydraflow 0.16.0__tar.gz → 0.16.2__tar.gz

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.
Files changed (100) hide show
  1. {hydraflow-0.16.0 → hydraflow-0.16.2}/PKG-INFO +2 -6
  2. {hydraflow-0.16.0 → hydraflow-0.16.2}/README.md +1 -5
  3. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part1-applications/main-decorator.md +30 -0
  4. {hydraflow-0.16.0 → hydraflow-0.16.2}/pyproject.toml +1 -1
  5. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/io.py +33 -15
  6. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/main.py +6 -1
  7. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/run_info.py +3 -25
  8. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/test_io.py +2 -2
  9. {hydraflow-0.16.0 → hydraflow-0.16.2}/.devcontainer/devcontainer.json +0 -0
  10. {hydraflow-0.16.0 → hydraflow-0.16.2}/.devcontainer/postCreate.sh +0 -0
  11. {hydraflow-0.16.0 → hydraflow-0.16.2}/.devcontainer/starship.toml +0 -0
  12. {hydraflow-0.16.0 → hydraflow-0.16.2}/.gitattributes +0 -0
  13. {hydraflow-0.16.0 → hydraflow-0.16.2}/.github/workflows/ci.yaml +0 -0
  14. {hydraflow-0.16.0 → hydraflow-0.16.2}/.github/workflows/docs.yaml +0 -0
  15. {hydraflow-0.16.0 → hydraflow-0.16.2}/.github/workflows/publish.yaml +0 -0
  16. {hydraflow-0.16.0 → hydraflow-0.16.2}/.gitignore +0 -0
  17. {hydraflow-0.16.0 → hydraflow-0.16.2}/LICENSE +0 -0
  18. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/getting-started/concepts.md +0 -0
  19. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/getting-started/index.md +0 -0
  20. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/getting-started/installation.md +0 -0
  21. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/index.md +0 -0
  22. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part1-applications/configuration.md +0 -0
  23. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part1-applications/execution.md +0 -0
  24. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part1-applications/index.md +0 -0
  25. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part2-advanced/index.md +0 -0
  26. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part2-advanced/job-configuration.md +0 -0
  27. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part2-advanced/sweep-syntax.md +0 -0
  28. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part3-analysis/index.md +0 -0
  29. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part3-analysis/run-class.md +0 -0
  30. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part3-analysis/run-collection.md +0 -0
  31. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/part3-analysis/updating-runs.md +0 -0
  32. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/practical-tutorials/advanced.md +0 -0
  33. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/practical-tutorials/analysis.md +0 -0
  34. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/practical-tutorials/applications.md +0 -0
  35. {hydraflow-0.16.0 → hydraflow-0.16.2}/docs/practical-tutorials/index.md +0 -0
  36. {hydraflow-0.16.0 → hydraflow-0.16.2}/examples/example.py +0 -0
  37. {hydraflow-0.16.0 → hydraflow-0.16.2}/examples/hydraflow.yaml +0 -0
  38. {hydraflow-0.16.0 → hydraflow-0.16.2}/examples/submit.py +0 -0
  39. {hydraflow-0.16.0 → hydraflow-0.16.2}/mkdocs.yaml +0 -0
  40. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/__init__.py +0 -0
  41. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/cli.py +0 -0
  42. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/__init__.py +0 -0
  43. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/context.py +0 -0
  44. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/run.py +0 -0
  45. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/core/run_collection.py +0 -0
  46. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/__init__.py +0 -0
  47. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/aio.py +0 -0
  48. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/conf.py +0 -0
  49. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/io.py +0 -0
  50. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/job.py +0 -0
  51. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/executor/parser.py +0 -0
  52. {hydraflow-0.16.0 → hydraflow-0.16.2}/src/hydraflow/py.typed +0 -0
  53. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/__init__.py +0 -0
  54. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/__init__.py +0 -0
  55. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/app.py +0 -0
  56. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/conftest.py +0 -0
  57. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/hydraflow.yaml +0 -0
  58. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/submit.py +0 -0
  59. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/test_run.py +0 -0
  60. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/test_setup.py +0 -0
  61. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/test_show.py +0 -0
  62. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/cli/test_version.py +0 -0
  63. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/conftest.py +0 -0
  64. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/__init__.py +0 -0
  65. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/__init__.py +0 -0
  66. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/chdir.py +0 -0
  67. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/log_run.py +0 -0
  68. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/start_run.py +0 -0
  69. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/test_chdir.py +0 -0
  70. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/test_log_run.py +0 -0
  71. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/context/test_start_run.py +0 -0
  72. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/__init__.py +0 -0
  73. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/default.py +0 -0
  74. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/force_new_run.py +0 -0
  75. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/match_overrides.py +0 -0
  76. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/rerun_finished.py +0 -0
  77. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/skip_finished.py +0 -0
  78. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_default.py +0 -0
  79. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_force_new_run.py +0 -0
  80. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_main.py +0 -0
  81. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_match_overrides.py +0 -0
  82. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_rerun_finished.py +0 -0
  83. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_skip_finished.py +0 -0
  84. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/test_update.py +0 -0
  85. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/main/update.py +0 -0
  86. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/run/__init__.py +0 -0
  87. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/run/run.py +0 -0
  88. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/run/test_run.py +0 -0
  89. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/run/test_run_collection.py +0 -0
  90. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/core/run/test_run_info.py +0 -0
  91. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/__init__.py +0 -0
  92. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/conftest.py +0 -0
  93. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/echo.py +0 -0
  94. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/read.py +0 -0
  95. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_aio.py +0 -0
  96. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_args.py +0 -0
  97. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_conf.py +0 -0
  98. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_io.py +0 -0
  99. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_job.py +0 -0
  100. {hydraflow-0.16.0 → hydraflow-0.16.2}/tests/executor/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hydraflow
3
- Version: 0.16.0
3
+ Version: 0.16.2
4
4
  Summary: HydraFlow seamlessly integrates Hydra and MLflow to streamline ML experiment management, combining Hydra's configuration management with MLflow's tracking capabilities.
5
5
  Project-URL: Documentation, https://daizutabi.github.io/hydraflow/
6
6
  Project-URL: Source, https://github.com/daizutabi/hydraflow
@@ -194,10 +194,6 @@ For detailed documentation, visit our [documentation site](https://daizutabi.git
194
194
  - [User Guide](https://daizutabi.github.io/hydraflow/part1-applications/) - Detailed documentation of HydraFlow's capabilities
195
195
  - [API Reference](https://daizutabi.github.io/hydraflow/api/hydraflow/) - Complete API documentation
196
196
 
197
- ## Contributing
198
-
199
- We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) for details.
200
-
201
197
  ## License
202
198
 
203
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
199
+ This project is licensed under the MIT License.
@@ -141,10 +141,6 @@ For detailed documentation, visit our [documentation site](https://daizutabi.git
141
141
  - [User Guide](https://daizutabi.github.io/hydraflow/part1-applications/) - Detailed documentation of HydraFlow's capabilities
142
142
  - [API Reference](https://daizutabi.github.io/hydraflow/api/hydraflow/) - Complete API documentation
143
143
 
144
- ## Contributing
145
-
146
- We welcome contributions! Please see our [contributing guide](CONTRIBUTING.md) for details.
147
-
148
144
  ## License
149
145
 
150
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
146
+ This project is licensed under the MIT License.
@@ -242,6 +242,36 @@ This option is particularly useful when:
242
242
  - You're iterating on experiments with command-line variations
243
243
  - Your configuration contains volatile or automatically generated values
244
244
 
245
+ ### Dynamic Configuration Updates (`update`)
246
+
247
+ Modify or enhance the configuration after it has been loaded by Hydra
248
+ but before the run starts:
249
+
250
+ ```python
251
+ def update_config(cfg: Config) -> Config:
252
+ # Calculate derived values or add runtime information
253
+ if cfg.width > 0 and cfg.height > 0:
254
+ cfg.area = cfg.width * cfg.height
255
+ return cfg
256
+
257
+ @hydraflow.main(Config, update=update_config)
258
+ def train(run: Run, cfg: Config) -> None:
259
+ # Configuration has been updated with calculated area
260
+ print(f"Area: {cfg.area}")
261
+ ```
262
+
263
+ This option is powerful when you need to:
264
+
265
+ - Calculate derived parameters based on existing configuration values
266
+ - Apply conditional logic to adjust parameters based on their relationships
267
+ - Ensure consistency between related parameters
268
+ - Adapt configurations to the current environment (e.g., hardware capabilities)
269
+
270
+ The `update` function should accept a configuration object and
271
+ return the same object (or None).
272
+ Any changes made to the configuration will be saved to the run's configuration file,
273
+ ensuring that the stored configuration accurately reflects all updates.
274
+
245
275
  ## Best Practices
246
276
 
247
277
  1. **Keep Configuration Classes Focused**: Break down complex configurations
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hydraflow"
7
- version = "0.16.0"
7
+ version = "0.16.2"
8
8
  description = "HydraFlow seamlessly integrates Hydra and MLflow to streamline ML experiment management, combining Hydra's configuration management with MLflow's tracking capabilities."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -5,9 +5,12 @@ from __future__ import annotations
5
5
  import fnmatch
6
6
  import urllib.parse
7
7
  import urllib.request
8
+ from functools import cache
8
9
  from pathlib import Path
9
10
  from typing import TYPE_CHECKING
10
11
 
12
+ from omegaconf import OmegaConf
13
+
11
14
  if TYPE_CHECKING:
12
15
  from collections.abc import Callable, Iterator
13
16
 
@@ -74,27 +77,37 @@ def log_text(run: Run, from_dir: Path, pattern: str = "*.log") -> None:
74
77
  mlflow.log_text(text, file.name)
75
78
 
76
79
 
77
- def get_experiment_name(path: Path) -> str | None:
78
- """Get the experiment name from the meta file."""
79
- metafile = path / "meta.yaml"
80
- if not metafile.exists():
81
- return None
82
- lines = metafile.read_text().splitlines()
83
- for line in lines:
84
- if line.startswith("name:"):
85
- return line.split(":")[1].strip()
86
- return None
80
+ @cache
81
+ def get_experiment_name(experiment_dir: Path) -> str:
82
+ """Get the job name from an experiment directory.
83
+
84
+ Extracts the job name from the meta.yaml file. Returns an empty string
85
+ if the file does not exist or if the job name cannot be found.
86
+
87
+ Args:
88
+ experiment_dir: Path to the experiment directory containing the meta.yaml file
89
+
90
+ Returns:
91
+ The job name as a string, or an empty string if the file does not exist
92
+
93
+ """
94
+ path = experiment_dir / "meta.yaml"
95
+ if not path.exists():
96
+ return ""
97
+
98
+ meta = OmegaConf.load(experiment_dir / "meta.yaml")
99
+ return OmegaConf.select(meta, "name", default="")
87
100
 
88
101
 
89
102
  def predicate_experiment_dir(
90
- path: Path,
103
+ experiment_dir: Path,
91
104
  experiment_names: list[str] | Callable[[str], bool] | None = None,
92
105
  ) -> bool:
93
106
  """Predicate an experiment directory based on the path and experiment names."""
94
- if not path.is_dir() or path.name in [".trash", "0"]:
107
+ if not experiment_dir.is_dir() or experiment_dir.name in [".trash", "0"]:
95
108
  return False
96
109
 
97
- name = get_experiment_name(path)
110
+ name = get_experiment_name(experiment_dir)
98
111
  if not name:
99
112
  return False
100
113
 
@@ -108,9 +121,14 @@ def predicate_experiment_dir(
108
121
 
109
122
 
110
123
  def get_experiment_names(tracking_dir: str | Path) -> list[str]:
111
- """Get the experiment names from the tracking directory."""
124
+ """Get the experiment names from the tracking directory.
125
+
126
+ Returns:
127
+ list[str]: A list of experiment names sorted by the name.
128
+
129
+ """
112
130
  names = [get_experiment_name(path) for path in Path(tracking_dir).iterdir()]
113
- return [name for name in names if name is not None and name != "Default"]
131
+ return sorted(name for name in names if name and name != "Default")
114
132
 
115
133
 
116
134
  def iter_experiment_dirs(
@@ -82,7 +82,12 @@ def main[C](
82
82
  rerun_finished: If True, allows rerunning completed runs. Defaults to
83
83
  False.
84
84
  update: A function that takes a configuration and returns a new
85
- configuration. Defaults to None.
85
+ configuration or None. The function can modify the configuration in-place
86
+ and/or return it. If the function returns None, the original (potentially
87
+ modified) configuration is used. Changes made by this function are saved
88
+ to the configuration file. This is useful for adding derived parameters,
89
+ ensuring consistency between related values, or adding runtime information
90
+ to the configuration. Defaults to None.
86
91
 
87
92
  """
88
93
  import mlflow
@@ -11,11 +11,11 @@ was created.
11
11
  from __future__ import annotations
12
12
 
13
13
  from dataclasses import dataclass
14
- from functools import cache, cached_property
14
+ from functools import cached_property
15
15
  from pathlib import Path
16
16
  from typing import TYPE_CHECKING
17
17
 
18
- from omegaconf import OmegaConf
18
+ from .io import get_experiment_name
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from pathlib import Path
@@ -50,7 +50,7 @@ class RunInfo:
50
50
  Hydra configuration file (e.g., if the file does not exist or does not
51
51
  contain the expected format).
52
52
  """
53
- return get_job_name(self.run_dir.parent)
53
+ return get_experiment_name(self.run_dir.parent)
54
54
 
55
55
  def to_dict(self) -> dict[str, Any]:
56
56
  """Convert the RunInfo to a dictionary."""
@@ -59,25 +59,3 @@ class RunInfo:
59
59
  "run_dir": self.run_dir.as_posix(),
60
60
  "job_name": self.job_name,
61
61
  }
62
-
63
-
64
- @cache
65
- def get_job_name(experiment_dir: Path) -> str:
66
- """Get the job name from an experiment directory.
67
-
68
- Extracts the job name from the meta.yaml file. Returns an empty string
69
- if the file does not exist or if the job name cannot be found.
70
-
71
- Args:
72
- experiment_dir: Path to the experiment directory containing the meta.yaml file
73
-
74
- Returns:
75
- The job name as a string, or an empty string if the file does not exist
76
-
77
- """
78
- path = experiment_dir / "meta.yaml"
79
- if not path.exists():
80
- return ""
81
-
82
- meta = OmegaConf.load(experiment_dir / "meta.yaml")
83
- return OmegaConf.select(meta, "name")
@@ -90,14 +90,14 @@ def test_predicate_experiment_dir():
90
90
  def test_get_experiment_name_none(tracking_dir: Path):
91
91
  from hydraflow.core.io import get_experiment_name
92
92
 
93
- assert get_experiment_name(tracking_dir.parent) is None
93
+ assert get_experiment_name(tracking_dir.parent) == ""
94
94
 
95
95
 
96
96
  def test_get_experiment_name_metafile_none(tracking_dir: Path):
97
97
  from hydraflow.core.io import get_experiment_name
98
98
 
99
99
  (tracking_dir / "meta.yaml").touch()
100
- assert get_experiment_name(tracking_dir) is None
100
+ assert get_experiment_name(tracking_dir) == ""
101
101
 
102
102
 
103
103
  def test_iter_run_dirs(tracking_dir: Path):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes