params-proto 3.2.4__tar.gz → 3.3.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.
- {params_proto-3.2.4 → params_proto-3.3.0}/PKG-INFO +1 -1
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/release_notes.md +18 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/pyproject.toml +1 -1
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/cli/cli_parse.py +169 -17
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_nested_cli.py +8 -15
- {params_proto-3.2.4 → params_proto-3.3.0}/.claude-plugin/marketplace.json +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.claude-plugin/plugin.json +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.editorconfig +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.gitignore +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/.gitignore +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/codeStyles/codeStyleConfig.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/markdown.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/misc.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/modules.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/params-proto.iml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/ruff.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/vcs.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.idea/workspace.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.readthedocs.yaml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.run/pytest for test_neo_proto_cli.run.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/.run/pytest in test_params_proto.run.xml +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/ANSI_HELP_CONSIDERATIONS.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/CLAUDE.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/LICENSE.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/Makefile +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/README +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/README.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/Makefile +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/api/hyper.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/api/proto.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/api/utils.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/advanced_features.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/basic_usage.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/environment_variables.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/hyperparameter_sweeps.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/index.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/nested_configs.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/quick_start.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_static/ansi.css +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/_static/custom.css +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/api/index.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/api/proto.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/conf.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/examples/basic_usage.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/examples/cli_applications.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/examples/ml_training.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/examples/rl_agent.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/index.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/advanced_patterns.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/ansi_formatting.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/cli-fundamentals.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/cli-patterns.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/configuration-patterns.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/core-concepts.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/environment_variables.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/help-generation.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/hyperparameter_sweeps.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/naming-conventions.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/parameter-iteration.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/parameter-overrides.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/type-system.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/union_types.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/key_concepts/welcome.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/migration.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/quick_start.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/docs/requirements.txt +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/examples/union_subcommands.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/figures/man-page.png +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/figures/params-proto-autocompletion.gif +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/figures/spec_files.png +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/demo_params_proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/demo_v3.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/proto_DAT_scratch.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/proto_dependency_tree_pattern.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/test_super.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/scratch/test_super_minimal.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/SKILL.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/references/cli-and-types.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/references/environment-vars.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/references/patterns.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/references/sweeps.md +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto.skill +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/app.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/cli/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/cli/ansi_help.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/cli/help_gen.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/documentation.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/envvar.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/hyper/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/hyper/proxies.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/hyper/sweep.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/parse_env_template.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/type_utils.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v1/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v1/hyper.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v1/params_proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v2/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v2/hyper.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v2/partial.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v2/proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/src/params_proto/v2/utils.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v1/__init__.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v1/test_hyper.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v1/test_params_proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_Eval.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_neo_hyper.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_neo_proto.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_neo_proto_cli.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_neo_proto_partial.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v2/test_utils.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/conftest.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/samples/train.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_advanced_types.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_class_level_methods.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_cli_parsing.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_help_strings.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_method_self_param.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_parse_env_template.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_piter.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_positional_example.sh +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_comments.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_context_manager.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_core.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_envvar.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_linebreaking.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_partial.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_proto_required.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_strings.py +0 -0
- {params_proto-3.2.4 → params_proto-3.3.0}/tests/test_v3/test_sweep.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: params-proto
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Modern Hyper Parameter Management for Machine Learning
|
|
5
5
|
Project-URL: Homepage, https://github.com/geyang/params-proto
|
|
6
6
|
Project-URL: Documentation, https://params-proto.readthedocs.io
|
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
This page contains the release history and changelog for params-proto.
|
|
4
4
|
|
|
5
|
+
## Version 3.3.0 (2025-02-04)
|
|
6
|
+
|
|
7
|
+
### ✨ Features
|
|
8
|
+
|
|
9
|
+
- **Deep Nested Dot Notation for CLI**: Support for overriding nested dataclass fields via CLI
|
|
10
|
+
```bash
|
|
11
|
+
# Before (not supported):
|
|
12
|
+
python train.py train-config --model.hidden-size 512 # error
|
|
13
|
+
|
|
14
|
+
# Now works:
|
|
15
|
+
python train.py train-config --epochs 200 --model.hidden-size 512 --model.num-layers 8
|
|
16
|
+
```
|
|
17
|
+
- Recursively detects nested dataclass fields in type annotations
|
|
18
|
+
- Supports arbitrary nesting depth (e.g., `--model.encoder.layers 12`)
|
|
19
|
+
- Automatically constructs nested dataclass instances
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
5
23
|
## Version 3.2.4 (2025-02-04)
|
|
6
24
|
|
|
7
25
|
### 🐛 Bug Fixes
|
|
@@ -83,6 +83,101 @@ def _get_required_fields(cls) -> List[str]:
|
|
|
83
83
|
return required
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
def _is_nested_dataclass(annotation) -> bool:
|
|
87
|
+
"""Check if annotation is a nested dataclass type."""
|
|
88
|
+
import dataclasses
|
|
89
|
+
|
|
90
|
+
# Skip primitive types and common non-dataclass types
|
|
91
|
+
if annotation in {int, str, float, bool, list, dict, tuple, set, Path, type(None)}:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
# Check if it's a type and a dataclass
|
|
95
|
+
if isinstance(annotation, type) and dataclasses.is_dataclass(annotation):
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _get_nested_attrs(cls, prefix: str = "") -> Dict[str, tuple]:
|
|
102
|
+
"""Recursively get all nested dataclass attributes.
|
|
103
|
+
|
|
104
|
+
Returns dict mapping dotted-kebab-name -> (dotted_underscore_path, leaf_type)
|
|
105
|
+
|
|
106
|
+
Example for TrainConfig with nested ModelConfig:
|
|
107
|
+
{
|
|
108
|
+
"model.hidden-size": ("model.hidden_size", int),
|
|
109
|
+
"model.num-layers": ("model.num_layers", int),
|
|
110
|
+
}
|
|
111
|
+
"""
|
|
112
|
+
import dataclasses
|
|
113
|
+
|
|
114
|
+
result = {}
|
|
115
|
+
annotations = getattr(cls, "__annotations__", {})
|
|
116
|
+
|
|
117
|
+
for attr_name, attr_type in annotations.items():
|
|
118
|
+
kebab_attr = attr_name.replace("_", "-")
|
|
119
|
+
full_kebab = f"{prefix}{kebab_attr}" if prefix else kebab_attr
|
|
120
|
+
full_underscore = f"{prefix.replace('-', '_')}{attr_name}" if prefix else attr_name
|
|
121
|
+
|
|
122
|
+
if _is_nested_dataclass(attr_type):
|
|
123
|
+
# Recursively get nested attributes
|
|
124
|
+
nested = _get_nested_attrs(attr_type, prefix=f"{full_kebab}.")
|
|
125
|
+
result.update(nested)
|
|
126
|
+
else:
|
|
127
|
+
# Leaf attribute
|
|
128
|
+
result[full_kebab] = (full_underscore, attr_type)
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _set_nested_value(d: dict, path: str, value: Any) -> None:
|
|
134
|
+
"""Set a value in a nested dict using dot notation path.
|
|
135
|
+
|
|
136
|
+
Example: _set_nested_value({}, "model.hidden_size", 512)
|
|
137
|
+
Results in {"model": {"hidden_size": 512}}
|
|
138
|
+
"""
|
|
139
|
+
parts = path.split(".")
|
|
140
|
+
current = d
|
|
141
|
+
for part in parts[:-1]:
|
|
142
|
+
if part not in current:
|
|
143
|
+
current[part] = {}
|
|
144
|
+
current = current[part]
|
|
145
|
+
current[parts[-1]] = value
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _build_nested_instance(cls, flat_attrs: dict, nested_attrs: dict):
|
|
149
|
+
"""Build a dataclass instance with nested dataclass fields.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
cls: The dataclass class to instantiate
|
|
153
|
+
flat_attrs: Dict of top-level attribute values
|
|
154
|
+
nested_attrs: Dict of nested dicts for nested dataclass fields
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Instance of cls with nested dataclasses properly constructed
|
|
158
|
+
"""
|
|
159
|
+
import dataclasses
|
|
160
|
+
|
|
161
|
+
final_attrs = dict(flat_attrs)
|
|
162
|
+
annotations = getattr(cls, "__annotations__", {})
|
|
163
|
+
|
|
164
|
+
for attr_name, attr_type in annotations.items():
|
|
165
|
+
if attr_name in nested_attrs and _is_nested_dataclass(attr_type):
|
|
166
|
+
# Recursively build nested dataclass
|
|
167
|
+
nested_data = nested_attrs[attr_name]
|
|
168
|
+
# Separate flat and nested for the nested class
|
|
169
|
+
nested_flat = {}
|
|
170
|
+
nested_nested = {}
|
|
171
|
+
for k, v in nested_data.items():
|
|
172
|
+
if isinstance(v, dict):
|
|
173
|
+
nested_nested[k] = v
|
|
174
|
+
else:
|
|
175
|
+
nested_flat[k] = v
|
|
176
|
+
final_attrs[attr_name] = _build_nested_instance(attr_type, nested_flat, nested_nested)
|
|
177
|
+
|
|
178
|
+
return cls(**final_attrs)
|
|
179
|
+
|
|
180
|
+
|
|
86
181
|
def _match_class_by_name(name: str, classes: list) -> Union[type, None]:
|
|
87
182
|
"""Match a string to one of the Union classes.
|
|
88
183
|
|
|
@@ -193,6 +288,7 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
193
288
|
# Build unprefixed union attribute map for classes NOT decorated with @proto.prefix
|
|
194
289
|
# Maps attr-name -> (union_param_name, attr_name_underscore)
|
|
195
290
|
# Classes in _SINGLETONS are @proto.prefix decorated and require prefixed attrs
|
|
291
|
+
# Also includes nested dataclass attributes (e.g., "model.hidden-size")
|
|
196
292
|
unprefixed_attrs = {}
|
|
197
293
|
for kebab_name, (param_name, union_classes) in union_params.items():
|
|
198
294
|
for cls in union_classes:
|
|
@@ -201,12 +297,20 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
201
297
|
if is_prefix_class:
|
|
202
298
|
continue
|
|
203
299
|
if hasattr(cls, "__annotations__"):
|
|
204
|
-
for attr_name in cls.__annotations__:
|
|
300
|
+
for attr_name, attr_type in cls.__annotations__.items():
|
|
205
301
|
kebab_attr = attr_name.replace("_", "-")
|
|
206
302
|
# Map to the union param (first one wins if multiple unions have same attr)
|
|
207
303
|
if kebab_attr not in unprefixed_attrs:
|
|
208
304
|
unprefixed_attrs[kebab_attr] = (param_name, attr_name)
|
|
209
305
|
|
|
306
|
+
# Check for nested dataclass and add its attributes
|
|
307
|
+
if _is_nested_dataclass(attr_type):
|
|
308
|
+
nested_attrs = _get_nested_attrs(attr_type, prefix=f"{kebab_attr}.")
|
|
309
|
+
for nested_kebab, (nested_path, nested_type) in nested_attrs.items():
|
|
310
|
+
full_path = f"{attr_name}.{nested_path.split('.', 1)[1] if '.' in nested_path else nested_path}"
|
|
311
|
+
if nested_kebab not in unprefixed_attrs:
|
|
312
|
+
unprefixed_attrs[nested_kebab] = (param_name, full_path)
|
|
313
|
+
|
|
210
314
|
# Parse arguments
|
|
211
315
|
result = {}
|
|
212
316
|
prefix_values = {} # (singleton, param_name) -> value
|
|
@@ -451,21 +555,65 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
451
555
|
# Instantiate Union classes with collected attributes
|
|
452
556
|
for param_name, selected_class in union_selections.items():
|
|
453
557
|
# Collect attributes for this Union parameter
|
|
454
|
-
|
|
455
|
-
|
|
558
|
+
# Separate flat (top-level) and nested attributes
|
|
559
|
+
flat_attrs = {}
|
|
560
|
+
nested_attrs = {} # For nested dataclass fields
|
|
561
|
+
|
|
562
|
+
for (union_param, attr_path), value_str in union_attrs.items():
|
|
456
563
|
if union_param == param_name:
|
|
457
|
-
#
|
|
458
|
-
if
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
564
|
+
# Check if this is a nested path (contains dots)
|
|
565
|
+
if "." in attr_path:
|
|
566
|
+
# Nested attribute like "model.hidden_size"
|
|
567
|
+
parts = attr_path.split(".")
|
|
568
|
+
top_level = parts[0]
|
|
569
|
+
rest_path = ".".join(parts[1:])
|
|
570
|
+
|
|
571
|
+
# Get the type of the nested field
|
|
572
|
+
if hasattr(selected_class, "__annotations__"):
|
|
573
|
+
top_type = selected_class.__annotations__.get(top_level)
|
|
574
|
+
if top_type and _is_nested_dataclass(top_type):
|
|
575
|
+
# Find the leaf type by traversing the path
|
|
576
|
+
current_type = top_type
|
|
577
|
+
for part in parts[1:]:
|
|
578
|
+
if hasattr(current_type, "__annotations__"):
|
|
579
|
+
current_type = current_type.__annotations__.get(part, str)
|
|
580
|
+
else:
|
|
581
|
+
current_type = str
|
|
582
|
+
break
|
|
583
|
+
|
|
584
|
+
try:
|
|
585
|
+
value = _convert_type(value_str, current_type)
|
|
586
|
+
except (ValueError, TypeError):
|
|
587
|
+
raise SystemExit(
|
|
588
|
+
f"error: invalid value for --{attr_path.replace('_', '-')}: {value_str}"
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# Store in nested structure
|
|
592
|
+
if top_level not in nested_attrs:
|
|
593
|
+
nested_attrs[top_level] = {}
|
|
594
|
+
_set_nested_value(nested_attrs[top_level], rest_path, value)
|
|
595
|
+
continue
|
|
596
|
+
|
|
597
|
+
# Fallback: treat as string
|
|
598
|
+
if top_level not in nested_attrs:
|
|
599
|
+
nested_attrs[top_level] = {}
|
|
600
|
+
_set_nested_value(nested_attrs[top_level], rest_path, value_str)
|
|
466
601
|
else:
|
|
467
|
-
#
|
|
468
|
-
|
|
602
|
+
# Top-level attribute
|
|
603
|
+
if hasattr(selected_class, "__annotations__"):
|
|
604
|
+
attr_type = selected_class.__annotations__.get(attr_path, str)
|
|
605
|
+
try:
|
|
606
|
+
flat_attrs[attr_path] = _convert_type(value_str, attr_type)
|
|
607
|
+
except (ValueError, TypeError):
|
|
608
|
+
raise SystemExit(
|
|
609
|
+
f"error: invalid value for --{param_name.replace('_', '-')}.{attr_path.replace('_', '-')}: {value_str}"
|
|
610
|
+
)
|
|
611
|
+
else:
|
|
612
|
+
# No annotations, treat as string
|
|
613
|
+
flat_attrs[attr_path] = value_str
|
|
614
|
+
|
|
615
|
+
# Merge flat_attrs into attrs for compatibility with existing code
|
|
616
|
+
attrs = flat_attrs
|
|
469
617
|
|
|
470
618
|
# Assign positional args to required fields of the selected class
|
|
471
619
|
if param_name in union_positional and union_positional[param_name]:
|
|
@@ -508,17 +656,21 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
508
656
|
attrs[key] = value
|
|
509
657
|
break
|
|
510
658
|
|
|
511
|
-
# Check for missing required fields
|
|
659
|
+
# Check for missing required fields (only check top-level, nested have defaults)
|
|
512
660
|
required_fields = _get_required_fields(selected_class)
|
|
513
661
|
for field_name in required_fields:
|
|
514
|
-
if field_name not in attrs:
|
|
662
|
+
if field_name not in attrs and field_name not in nested_attrs:
|
|
515
663
|
raise SystemExit(
|
|
516
664
|
f"error: {selected_class.__name__} requires argument: {field_name}"
|
|
517
665
|
)
|
|
518
666
|
|
|
519
667
|
# Instantiate the class with collected attributes
|
|
520
668
|
try:
|
|
521
|
-
|
|
669
|
+
if nested_attrs:
|
|
670
|
+
# Build with nested dataclass support
|
|
671
|
+
instance = _build_nested_instance(selected_class, attrs, nested_attrs)
|
|
672
|
+
else:
|
|
673
|
+
instance = selected_class(**attrs)
|
|
522
674
|
result[param_name] = instance
|
|
523
675
|
except TypeError as e:
|
|
524
676
|
raise SystemExit(f"error: failed to instantiate {selected_class.__name__}: {e}")
|
|
@@ -171,7 +171,7 @@ class TestThreeLayerNesting:
|
|
|
171
171
|
main()
|
|
172
172
|
""")
|
|
173
173
|
|
|
174
|
-
#
|
|
174
|
+
# Override nested model config via deep dot notation
|
|
175
175
|
# Plain dataclasses use unprefixed syntax (--epochs, not --config.epochs)
|
|
176
176
|
# Only @proto.prefix decorated classes require prefixed syntax
|
|
177
177
|
result = run_cli(
|
|
@@ -180,25 +180,18 @@ class TestThreeLayerNesting:
|
|
|
180
180
|
"train-config", # subcommand name (kebab-case)
|
|
181
181
|
"--epochs",
|
|
182
182
|
"200",
|
|
183
|
-
"--model.
|
|
183
|
+
"--model.hidden-size",
|
|
184
184
|
"512",
|
|
185
|
-
"--model.
|
|
185
|
+
"--model.num-layers",
|
|
186
186
|
"8",
|
|
187
187
|
],
|
|
188
|
-
expect_error=True, # Deep nested dot notation may not be supported yet
|
|
189
188
|
)
|
|
190
189
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
assert lines[2] == "model.num_layers=8"
|
|
197
|
-
else:
|
|
198
|
-
# If it failed, this documents the current limitation
|
|
199
|
-
pytest.skip(
|
|
200
|
-
"Deep nested dot notation (--model.hidden_size) not yet supported"
|
|
201
|
-
)
|
|
190
|
+
assert result["returncode"] == 0, f"CLI failed: {result['stderr']}"
|
|
191
|
+
lines = result["stdout"].strip().split("\n")
|
|
192
|
+
assert lines[0] == "epochs=200"
|
|
193
|
+
assert lines[1] == "model.hidden_size=512"
|
|
194
|
+
assert lines[2] == "model.num_layers=8"
|
|
202
195
|
|
|
203
196
|
|
|
204
197
|
class TestThreeLayerUnionNesting:
|
|
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
|
|
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
|
|
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
|
{params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/environment_variables.md
RENAMED
|
File without changes
|
{params_proto-3.2.4 → params_proto-3.3.0}/docs/_archive_v2/examples/hyperparameter_sweeps.md
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{params_proto-3.2.4 → params_proto-3.3.0}/skills/params-proto/references/environment-vars.md
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|