hydra-fast 0.1.0__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.
- hydra_fast-0.1.0/LICENSE +40 -0
- hydra_fast-0.1.0/PKG-INFO +362 -0
- hydra_fast-0.1.0/README.md +324 -0
- hydra_fast-0.1.0/pyproject.toml +230 -0
- hydra_fast-0.1.0/setup.cfg +4 -0
- hydra_fast-0.1.0/src/hydra_fast/__init__.py +157 -0
- hydra_fast-0.1.0/src/hydra_fast/_cache.py +302 -0
- hydra_fast-0.1.0/src/hydra_fast/_copy.py +40 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/__init__.py +0 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/config_loader.py +555 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/config_repository.py +216 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/config_search_path.py +111 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/config_source.py +602 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/default_element.py +585 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/defaults_list.py +1145 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/fingerprint.py +158 -0
- hydra_fast-0.1.0/src/hydra_fast/_internal/job_runtime.py +30 -0
- hydra_fast-0.1.0/src/hydra_fast/_structured.py +120 -0
- hydra_fast-0.1.0/src/hydra_fast/_typing.py +405 -0
- hydra_fast-0.1.0/src/hydra_fast/_yaml.py +294 -0
- hydra_fast-0.1.0/src/hydra_fast/api.py +278 -0
- hydra_fast-0.1.0/src/hydra_fast/compat/__init__.py +11 -0
- hydra_fast-0.1.0/src/hydra_fast/compat/oc_utils.py +287 -0
- hydra_fast-0.1.0/src/hydra_fast/compat/omegaconf_shim.py +394 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/__init__.py +158 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/env/default.yaml +0 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/help/default.yaml +34 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/hydra_help/default.yaml +25 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/hydra_logging/default.yaml +18 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/hydra_logging/disabled.yaml +4 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/hydra_logging/hydra_debug.yaml +22 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/hydra_logging/none.yaml +3 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/job_logging/default.yaml +20 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/job_logging/disabled.yaml +4 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/job_logging/none.yaml +3 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/job_logging/stdout.yaml +15 -0
- hydra_fast-0.1.0/src/hydra_fast/conf/hydra/output/default.yaml +7 -0
- hydra_fast-0.1.0/src/hydra_fast/container.py +1209 -0
- hydra_fast-0.1.0/src/hydra_fast/core/__init__.py +1 -0
- hydra_fast-0.1.0/src/hydra_fast/core/config_store.py +155 -0
- hydra_fast-0.1.0/src/hydra_fast/core/object_type.py +8 -0
- hydra_fast-0.1.0/src/hydra_fast/core/override_parser/__init__.py +1 -0
- hydra_fast-0.1.0/src/hydra_fast/core/override_parser/overrides_parser.py +99 -0
- hydra_fast-0.1.0/src/hydra_fast/core/singleton.py +25 -0
- hydra_fast-0.1.0/src/hydra_fast/errors.py +183 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/__init__.py +17 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/antlr_errors.py +99 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/functions.py +134 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/interpolation.py +993 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/override.py +1444 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/override_functions.py +361 -0
- hydra_fast-0.1.0/src/hydra_fast/grammar/utils.py +44 -0
- hydra_fast-0.1.0/src/hydra_fast/merge.py +219 -0
- hydra_fast-0.1.0/src/hydra_fast/nodes.py +274 -0
- hydra_fast-0.1.0/src/hydra_fast/omegaconf_api.py +832 -0
- hydra_fast-0.1.0/src/hydra_fast/py.typed +0 -0
- hydra_fast-0.1.0/src/hydra_fast/resolvers.py +263 -0
- hydra_fast-0.1.0/src/hydra_fast/utils.py +348 -0
- hydra_fast-0.1.0/src/hydra_fast/version.py +137 -0
- hydra_fast-0.1.0/src/hydra_fast.egg-info/PKG-INFO +362 -0
- hydra_fast-0.1.0/src/hydra_fast.egg-info/SOURCES.txt +81 -0
- hydra_fast-0.1.0/src/hydra_fast.egg-info/dependency_links.txt +1 -0
- hydra_fast-0.1.0/src/hydra_fast.egg-info/requires.txt +12 -0
- hydra_fast-0.1.0/src/hydra_fast.egg-info/top_level.txt +1 -0
- hydra_fast-0.1.0/tests/test_analysis.py +214 -0
- hydra_fast-0.1.0/tests/test_attrs.py +153 -0
- hydra_fast-0.1.0/tests/test_cache.py +329 -0
- hydra_fast-0.1.0/tests/test_compoconf.py +373 -0
- hydra_fast-0.1.0/tests/test_compose.py +590 -0
- hydra_fast-0.1.0/tests/test_container_api.py +890 -0
- hydra_fast-0.1.0/tests/test_defaults_list.py +157 -0
- hydra_fast-0.1.0/tests/test_errors.py +376 -0
- hydra_fast-0.1.0/tests/test_instantiate.py +147 -0
- hydra_fast-0.1.0/tests/test_internals.py +192 -0
- hydra_fast-0.1.0/tests/test_interpolation.py +246 -0
- hydra_fast-0.1.0/tests/test_nodes.py +172 -0
- hydra_fast-0.1.0/tests/test_overrides.py +167 -0
- hydra_fast-0.1.0/tests/test_packaging.py +143 -0
- hydra_fast-0.1.0/tests/test_realistic_tree.py +345 -0
- hydra_fast-0.1.0/tests/test_shim.py +131 -0
- hydra_fast-0.1.0/tests/test_typing.py +179 -0
- hydra_fast-0.1.0/tests/test_upstream.py +220 -0
- hydra_fast-0.1.0/tests/test_version_base.py +108 -0
hydra_fast-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) the hydra-fast authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
--------------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
This project is a derivative work of Hydra and OmegaConf. See ATTRIBUTION/
|
|
26
|
+
for the full upstream license texts:
|
|
27
|
+
|
|
28
|
+
* Hydra (hydra-core) -- Copyright (c) Facebook, Inc. and its affiliates.
|
|
29
|
+
MIT License. See ATTRIBUTION/LICENSE-hydra.
|
|
30
|
+
Portions of hydra-fast are ported from Hydra, in particular the Defaults
|
|
31
|
+
List algorithm (src/hydra_fast/_internal/defaults_list.py and
|
|
32
|
+
src/hydra_fast/_internal/default_element.py), Hydra's configuration schema
|
|
33
|
+
(src/hydra_fast/conf/), and the config search path, repository and loader
|
|
34
|
+
structure.
|
|
35
|
+
|
|
36
|
+
* OmegaConf -- Copyright (c) 2018, Omry Yadan. BSD 3-Clause License.
|
|
37
|
+
See ATTRIBUTION/LICENSE-omegaconf.
|
|
38
|
+
hydra-fast reimplements OmegaConf's container API, interpolation grammar
|
|
39
|
+
and merge semantics; the grammar token rules and reduction behaviour are
|
|
40
|
+
transcribed from OmegaConf's ANTLR grammar and visitor.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hydra-fast
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hydra-compatible config composition, 10-100x faster, with process-wide file caching
|
|
5
|
+
Author: the hydra-fast authors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kpoeppel/hydra_fast
|
|
8
|
+
Project-URL: Repository, https://github.com/kpoeppel/hydra_fast
|
|
9
|
+
Project-URL: Changelog, https://github.com/kpoeppel/hydra_fast/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/kpoeppel/hydra_fast/issues
|
|
11
|
+
Keywords: hydra,omegaconf,configuration,yaml,sweep,hyperparameter
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
24
|
+
Classifier: Operating System :: MacOS
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: PyYAML>=5.1
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest<8.4,>=7.0; extra == "test"
|
|
31
|
+
Requires-Dist: hydra-core<1.4,>=1.3; extra == "test"
|
|
32
|
+
Requires-Dist: omegaconf<2.4,>=2.3; extra == "test"
|
|
33
|
+
Provides-Extra: attrs
|
|
34
|
+
Requires-Dist: attrs>=21.0; extra == "attrs"
|
|
35
|
+
Provides-Extra: compoconf
|
|
36
|
+
Requires-Dist: compoconf>=0.1.16; extra == "compoconf"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# hydra-fast
|
|
40
|
+
|
|
41
|
+
[](https://github.com/kpoeppel/hydra_fast/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/hydra-fast/)
|
|
43
|
+
[](https://github.com/kpoeppel/hydra_fast/blob/main/LICENSE)
|
|
44
|
+
|
|
45
|
+
Hydra-compatible config composition, built for the case Hydra is slowest at:
|
|
46
|
+
composing the same config tree hundreds of times to build a parameter sweep.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from hydra_fast import compose, initialize_config_dir
|
|
50
|
+
|
|
51
|
+
with initialize_config_dir(config_dir="/path/to/conf", version_base=None):
|
|
52
|
+
cfg = compose(config_name="config", overrides=["db=postgres", "db.port=5433"])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Same config file syntax, same `${...}` interpolation grammar, same override
|
|
56
|
+
grammar, same `OmegaConf`/`compose` API. Roughly 100x faster on a sweep.
|
|
57
|
+
|
|
58
|
+
It reimplements the composition and interpolation core — including
|
|
59
|
+
`instantiate`/`_target_` — but not job launching or sweeper plugins. Within
|
|
60
|
+
that scope it is validated against the
|
|
61
|
+
real packages — including running **hydra's own override-grammar suite** and
|
|
62
|
+
**omegaconf's own grammar corpus** against it.
|
|
63
|
+
[docs/compatibility.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/compatibility.md) is explicit about the boundary.
|
|
64
|
+
|
|
65
|
+
## The numbers
|
|
66
|
+
|
|
67
|
+
Composing one config tree once per sweep point, then fully resolving it —
|
|
68
|
+
which is what building a sweep does. `hydra 1.3.2 + omegaconf 2.3.0`,
|
|
69
|
+
CPython 3.12, Linux, best-of-N wall clock.
|
|
70
|
+
|
|
71
|
+
**A 75-point sweep over a real production config tree** ([OpenEuroLLM
|
|
72
|
+
`oellm-autoexp`](https://github.com/OpenEuroLLM/oellm-autoexp): 239 YAML
|
|
73
|
+
files, deep group nesting, ~30 custom resolvers):
|
|
74
|
+
|
|
75
|
+
| | total | per point |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| hydra + omegaconf | **23.0 s** | 306 ms |
|
|
78
|
+
| hydra-fast | **0.19 s** | 2.5 ms |
|
|
79
|
+
|
|
80
|
+
Seconds to milliseconds — **~120x**.
|
|
81
|
+
|
|
82
|
+
Per point on the same tree, by configuration:
|
|
83
|
+
|
|
84
|
+
| | per point | vs stock |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| hydra + omegaconf | 306 ms | 1x |
|
|
87
|
+
| hydra + omegaconf, with a hand-written cache layer monkeypatched in [^1] | 71 ms | 4x |
|
|
88
|
+
| **hydra-fast** | **2.5 ms** | **~120x** |
|
|
89
|
+
|
|
90
|
+
The default `validation="stat"` — which re-checks the filesystem on every cache
|
|
91
|
+
lookup, so a config edited mid-process is picked up — costs nothing measurable
|
|
92
|
+
here: best-of-5 over this sweep gives 2.72 ms/point with validation and
|
|
93
|
+
2.78 ms/point without. The validation work is amortized to a handful of
|
|
94
|
+
`stat()` calls per composition (see
|
|
95
|
+
[docs/architecture.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/architecture.md)), so there is no safety-for-speed
|
|
96
|
+
trade to make. Keep the default.
|
|
97
|
+
|
|
98
|
+
[^1]: `oellm-autoexp` ships seven monkeypatched cache layers over Hydra
|
|
99
|
+
(`hydra_staged_sweep/config/cache.py`). hydra-fast is ~25x faster than
|
|
100
|
+
that, because caching alone cannot get past Hydra's object model — see
|
|
101
|
+
[docs/architecture.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/architecture.md).
|
|
102
|
+
|
|
103
|
+
Across repeated runs on a shared machine the speedup landed between **60x and
|
|
104
|
+
130x**, with the low end being runs where the box was busy. Treat ~100x as the
|
|
105
|
+
expected figure and measure on your own tree; the ratio depends on how much of
|
|
106
|
+
your config is interpolation (where hydra-fast wins most) versus custom
|
|
107
|
+
resolvers doing real work (where it cannot help).
|
|
108
|
+
|
|
109
|
+
Reproduce without any private config tree:
|
|
110
|
+
|
|
111
|
+
```console
|
|
112
|
+
$ python bench/bench_synthetic.py --points 100 --repeat 3
|
|
113
|
+
tree: 25 yaml files, 400 keys in base, 6 groups x 4 options; 100 sweep points
|
|
114
|
+
equivalence on first 3 points: OK
|
|
115
|
+
hydra+omegaconf total= 46762.6 ms per_point= 467.63 ms
|
|
116
|
+
hydra-fast total= 368.6 ms per_point= 3.69 ms
|
|
117
|
+
|
|
118
|
+
speedup: 126.9x
|
|
119
|
+
yaml parses: 13 (vs 161 reads requested)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The benchmark verifies both implementations produce identical output before
|
|
123
|
+
reporting a time.
|
|
124
|
+
|
|
125
|
+
## Why it is faster
|
|
126
|
+
|
|
127
|
+
Three things, in rough order of impact. [docs/architecture.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/architecture.md)
|
|
128
|
+
has the detail.
|
|
129
|
+
|
|
130
|
+
**Each file is read and parsed once per process.** Stock Hydra re-reads and
|
|
131
|
+
re-parses every YAML file in the tree on *every* `compose()` call — about 150
|
|
132
|
+
parses per call on the tree above, so ~11,000 parses for a 75-point sweep.
|
|
133
|
+
hydra-fast parses each file once: 9 parses for the whole sweep. This was the
|
|
134
|
+
single biggest cost, about half of stock Hydra's time.
|
|
135
|
+
|
|
136
|
+
**Interpolations compile to closures.** OmegaConf re-walks an ANTLR parse tree
|
|
137
|
+
every time it resolves `${a.b}`. hydra-fast has a hand-written parser that
|
|
138
|
+
compiles each distinct string *once* into a Python closure, cached on the
|
|
139
|
+
string. A sweep typically has a few dozen distinct interpolation strings and
|
|
140
|
+
resolves them thousands of times. (Having no parse tree costs nothing
|
|
141
|
+
functionally: parse-once-resolve-many works the same way, and
|
|
142
|
+
`analyze_interpolation()` recovers static introspection — see
|
|
143
|
+
[docs/architecture.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/architecture.md#what-replaces-the-parse-tree).)
|
|
144
|
+
|
|
145
|
+
**Configs are plain dicts.** OmegaConf wraps every value in a `Node` object
|
|
146
|
+
with its own metadata and parent pointer. A 600-key config is 600+ objects to
|
|
147
|
+
allocate, link, and later deep-copy. In hydra-fast the storage *is* a
|
|
148
|
+
`dict`/`list` tree and `DictConfig` is a two-slot view over it, so merging is a
|
|
149
|
+
dict walk and copying is a data copy.
|
|
150
|
+
|
|
151
|
+
On top of that, the defaults list and the merged config are memoized on exactly
|
|
152
|
+
what can change them — notably *not* on the `++key=value` overrides a sweep
|
|
153
|
+
varies per point, which cannot affect either.
|
|
154
|
+
|
|
155
|
+
## Correctness
|
|
156
|
+
|
|
157
|
+
Speed is only interesting if the answer is the same. Every result is checked
|
|
158
|
+
against the real Hydra and OmegaConf:
|
|
159
|
+
|
|
160
|
+
```console
|
|
161
|
+
$ pytest
|
|
162
|
+
1022 passed, 3 skipped
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
1025 tests, and **no test needs anything outside the repository**. The three
|
|
166
|
+
that skip above need compoconf 0.3.1+, which CI installs. The upstream-suite
|
|
167
|
+
tests need the hydra and omegaconf source checkouts fetched (see *Running the
|
|
168
|
+
differential tests*); `HYDRA_FAST_REQUIRE_UPSTREAM=1` turns those skips into
|
|
169
|
+
errors, as CI does.
|
|
170
|
+
|
|
171
|
+
- **Upstream suites run against hydra-fast** (`tests/test_upstream.py`):
|
|
172
|
+
hydra's own `test_overrides_parser.py` — **508 cases, fully green**;
|
|
173
|
+
omegaconf's own grammar corpus driven through the public API — **131/131**;
|
|
174
|
+
omegaconf's `test_readonly.py`, `test_struct.py`, `test_oc_select.py` run
|
|
175
|
+
verbatim — **all green**.
|
|
176
|
+
- **70 structured-config typing cases** against an oracle captured from
|
|
177
|
+
omegaconf — coercion, rejection, `Optional`, `Enum`, `List[X]`, `Dict[K,V]`,
|
|
178
|
+
nested dataclasses, and through a full composition.
|
|
179
|
+
- **~60 interpolation cases** resolved through both OmegaConf and hydra-fast and
|
|
180
|
+
compared — node references, relative references (`${..a}`), nested keys
|
|
181
|
+
(`${a.${k}}`), escapes, quoting, every argument form, all built-in resolvers.
|
|
182
|
+
- **Exception types and messages** compared case by case, including omegaconf's
|
|
183
|
+
`full_key`/`object_type` trailer — and, for malformed input, ANTLR's own
|
|
184
|
+
phrasing reproduced without ANTLR: **744/744** inputs accepted or rejected
|
|
185
|
+
identically and **459/459** messages character-identical across 62 malformed
|
|
186
|
+
inputs x 12 grammar rules (`bench/oracle_errors.py`).
|
|
187
|
+
- **66 override cases** parsed by both Hydra's ANTLR parser and hydra-fast, with
|
|
188
|
+
the full parsed shape compared — type, key, package, value type, value.
|
|
189
|
+
- **33 composition cases** over 12 config trees covering `_self_` ordering,
|
|
190
|
+
`@package` headers, `override`/`optional` keywords, deletions, option lists,
|
|
191
|
+
interpolated group names, nested groups.
|
|
192
|
+
- **A realistic config tree that ships with the repository**
|
|
193
|
+
(`tests/realistic_config/`): nested groups, `# @package` headers named and
|
|
194
|
+
`_global_`, profiles using `override`, relative and root-reaching
|
|
195
|
+
interpolations, six custom resolvers, a `_target_`, and a `???` a sweep must
|
|
196
|
+
fill — twelve override shapes compared against hydra, plus a generated
|
|
197
|
+
large tree for scale. No private checkout needed.
|
|
198
|
+
- Cache-correctness tests: that a file edited mid-process is picked up, that
|
|
199
|
+
group overrides are not served a cached merge, and that one sweep point's
|
|
200
|
+
overrides cannot leak into another's.
|
|
201
|
+
- **Packaging and handoff tests**: that every shipped config file is tracked by
|
|
202
|
+
git (not just present on disk), and that the plain dict handed to an external
|
|
203
|
+
typing layer such as [compoconf](https://pypi.org/project/compoconf/) matches
|
|
204
|
+
hydra's in both values and types.
|
|
205
|
+
|
|
206
|
+
What this still does not prove: omegaconf's `test_grammar.py` asserts against
|
|
207
|
+
ANTLR parse trees, which hydra-fast has none of by design, and several
|
|
208
|
+
omegaconf suites import private internals that are only partially shimmed. Both
|
|
209
|
+
boundaries are itemised in
|
|
210
|
+
[docs/compatibility.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/compatibility.md) — treat it as the contract.
|
|
211
|
+
|
|
212
|
+
### Contributing
|
|
213
|
+
|
|
214
|
+
```console
|
|
215
|
+
$ pip install -e ".[test,attrs,compoconf]" pre-commit
|
|
216
|
+
$ pre-commit install
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`pre-commit run --all-files` runs what CI runs: ruff (lint and format), mypy,
|
|
220
|
+
pylint, bandit, yamllint, and the usual whitespace/YAML checks — all
|
|
221
|
+
configured in `pyproject.toml`, at 95 columns.
|
|
222
|
+
|
|
223
|
+
ruff stands in for black, flake8 and isort, which it subsumes. pylint is *not*
|
|
224
|
+
subsumed, so it runs as well: ruff's `PL` family covers most of it, the
|
|
225
|
+
overlap is disabled in `pyproject.toml`, and what remains are the checks ruff
|
|
226
|
+
has no equivalent for. pylint is clean at 10.00/10 and mypy reports no issues
|
|
227
|
+
across all 41 modules.
|
|
228
|
+
|
|
229
|
+
Two files are exempt from both the linter and the formatter:
|
|
230
|
+
`_internal/defaults_list.py` and `_internal/default_element.py` are ported from
|
|
231
|
+
hydra essentially verbatim so they stay diffable against upstream, and
|
|
232
|
+
reformatting them would defeat that.
|
|
233
|
+
|
|
234
|
+
### Running the differential tests
|
|
235
|
+
|
|
236
|
+
```console
|
|
237
|
+
$ pip install -e ".[test,attrs,compoconf]"
|
|
238
|
+
$ python .github/scripts/fetch_upstream.py # upstream suites + grammar corpus
|
|
239
|
+
$ pytest
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The second step matters. Upstream's own test suites and grammar corpus ship in
|
|
243
|
+
their sdists, not their wheels, so they are downloaded into a gitignored
|
|
244
|
+
`.upstream/`. Without them the strongest checks here **skip**, and a green run
|
|
245
|
+
means much less. Set `HYDRA_FAST_REQUIRE_UPSTREAM=1` to turn those skips into
|
|
246
|
+
errors — CI always does.
|
|
247
|
+
|
|
248
|
+
`bench/_sources.py` is the one place that decides where those trees live
|
|
249
|
+
(`HYDRA_SRC`, `OMEGACONF_SRC`, `OELLM_AUTOEXP`, else `.upstream/`) and which
|
|
250
|
+
versions are expected.
|
|
251
|
+
|
|
252
|
+
Two skips are expected and harmless: `tests/test_real_world.py` needs the
|
|
253
|
+
`oellm-autoexp` config tree, which is not public.
|
|
254
|
+
|
|
255
|
+
## Install
|
|
256
|
+
|
|
257
|
+
```console
|
|
258
|
+
pip install hydra-fast
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Only dependency is PyYAML. Install it built against libyaml (the usual binary
|
|
262
|
+
wheels are) and hydra-fast uses the C scanner automatically.
|
|
263
|
+
|
|
264
|
+
## Using it
|
|
265
|
+
|
|
266
|
+
### As itself
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
import hydra_fast
|
|
270
|
+
from hydra_fast import OmegaConf, compose, initialize_config_dir
|
|
271
|
+
|
|
272
|
+
with initialize_config_dir(config_dir="/abs/path/to/conf", version_base=None):
|
|
273
|
+
cfg = compose(config_name="config", overrides=["group=option", "++key=value"])
|
|
274
|
+
print(OmegaConf.to_yaml(cfg, resolve=True))
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`initialize`, `initialize_config_dir`, `initialize_config_module`, `compose`,
|
|
278
|
+
`main`, and the `OmegaConf` static API all match their Hydra/OmegaConf
|
|
279
|
+
counterparts.
|
|
280
|
+
|
|
281
|
+
### Under a library written against omegaconf
|
|
282
|
+
|
|
283
|
+
If a library you depend on does `from omegaconf import OmegaConf` to register
|
|
284
|
+
its resolvers, install the shim **before importing it**:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
import hydra_fast.compat.omegaconf_shim as shim
|
|
288
|
+
shim.install()
|
|
289
|
+
|
|
290
|
+
import that_library # its OmegaConf is now hydra-fast's
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`install()` is always explicit — importing `hydra_fast` never redirects
|
|
294
|
+
anything on its own. Only the public surface is shimmed; code reaching into
|
|
295
|
+
`omegaconf._utils` or `omegaconf.basecontainer` needs to target hydra-fast
|
|
296
|
+
directly.
|
|
297
|
+
|
|
298
|
+
### Cache control
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
import hydra_fast
|
|
302
|
+
|
|
303
|
+
hydra_fast.set_validation("never") # trust the first read; fastest
|
|
304
|
+
hydra_fast.clear_caches() # drop everything
|
|
305
|
+
hydra_fast.cache_stats() # hit/miss counters per layer
|
|
306
|
+
hydra_fast.get_validation() # "stat" or "never"
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`"stat"` is the default: every cache lookup re-`stat()`s the files it depends
|
|
310
|
+
on, so editing a config on disk mid-process invalidates exactly the entries
|
|
311
|
+
that depend on it. No explicit reload call is needed.
|
|
312
|
+
|
|
313
|
+
That covers more than edits. A file *added* to a group, a file *removed* from
|
|
314
|
+
one, a whole new group directory, and a changed `defaults:` list are all
|
|
315
|
+
picked up, because existence is cached against the parent directory's mtime --
|
|
316
|
+
which changes exactly when an entry is added or removed. `tests/test_cache.py`
|
|
317
|
+
pins each case.
|
|
318
|
+
|
|
319
|
+
`"never"` skips all of that and trusts the first read for the life of the
|
|
320
|
+
process, so a config edited afterwards is *not* seen; `clear_caches()` is the
|
|
321
|
+
only way to pick it up. `get_validation()` reports the current mode, which
|
|
322
|
+
matters if something else set it.
|
|
323
|
+
|
|
324
|
+
On the benchmarks above `"never"` is **not** measurably faster — the
|
|
325
|
+
validation is already amortized down to a few `stat()` calls per composition.
|
|
326
|
+
It exists for workloads with much larger config trees, where the per-file
|
|
327
|
+
checks could still show up. Measure before switching; the default is safe.
|
|
328
|
+
|
|
329
|
+
## Compatibility
|
|
330
|
+
|
|
331
|
+
Target is **hydra 1.3 / omegaconf 2.3** — the widely deployed pair.
|
|
332
|
+
[docs/compatibility.md](https://github.com/kpoeppel/hydra_fast/blob/main/docs/compatibility.md) lists what is covered, the few
|
|
333
|
+
deliberate deviations, and what is out of scope (job launching, sweeper
|
|
334
|
+
plugins, `instantiate`).
|
|
335
|
+
|
|
336
|
+
The short version: config **composition, parsing and syntax** are covered and
|
|
337
|
+
differentially tested. hydra-fast composes configs; it does not run jobs.
|
|
338
|
+
|
|
339
|
+
## Repository layout
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
src/hydra_fast/ the package
|
|
343
|
+
tests/ differential and unit tests
|
|
344
|
+
bench/ benchmarks and standalone differential checks
|
|
345
|
+
docs/ architecture.md, compatibility.md
|
|
346
|
+
ATTRIBUTION/ upstream licenses and what came from where
|
|
347
|
+
hydra/, omegaconf/ upstream source checkouts, for reference only
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The `hydra/` and `omegaconf/` directories at the root are upstream checkouts
|
|
351
|
+
kept for reading and diffing. They are **not** part of the distribution, and
|
|
352
|
+
they are not what the tests compare against — the tests use the *installed*
|
|
353
|
+
`hydra-core` / `omegaconf`. Note that with the repo root on `sys.path` those
|
|
354
|
+
directories shadow the installed packages as empty namespace packages;
|
|
355
|
+
`tests/conftest.py` drops the root from `sys.path` for exactly that reason.
|
|
356
|
+
|
|
357
|
+
## Licensing
|
|
358
|
+
|
|
359
|
+
MIT. hydra-fast is a derivative work of Hydra (MIT) and OmegaConf (BSD
|
|
360
|
+
3-Clause); the Defaults List algorithm in particular is ported from Hydra
|
|
361
|
+
rather than re-derived, because that algorithm *is* the composition spec. See
|
|
362
|
+
[ATTRIBUTION/](https://github.com/kpoeppel/hydra_fast/tree/main/ATTRIBUTION) for exactly what came from where.
|