params-proto 3.1.2__tar.gz → 3.2.1__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.1.2 → params_proto-3.2.1}/.claude-plugin/plugin.json +1 -1
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/workspace.xml +8 -5
- {params_proto-3.1.2 → params_proto-3.2.1}/PKG-INFO +1 -1
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/union_types.md +113 -43
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/release_notes.md +51 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/pyproject.toml +1 -1
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto/SKILL.md +2 -2
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto/references/cli-and-types.md +32 -2
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto.skill +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/cli/cli_parse.py +98 -1
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/hyper/sweep.py +71 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/proto.py +28 -16
- params_proto-3.2.1/tests/test_v3/conftest.py +56 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_cli_parsing.py +0 -55
- params_proto-3.2.1/tests/test_v3/test_nested_cli.py +956 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_piter.py +121 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.claude-plugin/marketplace.json +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.editorconfig +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.gitignore +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/.gitignore +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/codeStyles/codeStyleConfig.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/markdown.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/misc.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/modules.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/params-proto.iml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/ruff.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.idea/vcs.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.readthedocs.yaml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.run/pytest for test_neo_proto_cli.run.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/.run/pytest in test_params_proto.run.xml +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/ANSI_HELP_CONSIDERATIONS.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/CLAUDE.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/LICENSE.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/Makefile +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/README +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/README.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/Makefile +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/api/hyper.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/api/proto.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/api/utils.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/advanced_features.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/basic_usage.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/environment_variables.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/hyperparameter_sweeps.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/index.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/examples/nested_configs.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_archive_v2/quick_start.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_static/ansi.css +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/_static/custom.css +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/api/index.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/api/proto.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/conf.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/examples/basic_usage.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/examples/cli_applications.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/examples/ml_training.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/examples/rl_agent.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/index.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/advanced_patterns.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/ansi_formatting.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/cli-fundamentals.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/cli-patterns.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/configuration-patterns.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/core-concepts.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/environment_variables.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/help-generation.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/hyperparameter_sweeps.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/naming-conventions.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/parameter-iteration.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/parameter-overrides.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/type-system.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/key_concepts/welcome.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/migration.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/quick_start.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/docs/requirements.txt +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/examples/union_subcommands.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/figures/man-page.png +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/figures/params-proto-autocompletion.gif +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/figures/spec_files.png +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/scratch/demo_params_proto.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/scratch/demo_v3.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/scratch/proto_DAT_scratch.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/scratch/proto_dependency_tree_pattern.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto/references/environment-vars.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto/references/patterns.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/skills/params-proto/references/sweeps.md +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/app.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/cli/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/cli/ansi_help.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/cli/help_gen.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/documentation.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/envvar.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/hyper/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/hyper/proxies.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/parse_env_template.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/type_utils.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v1/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v1/hyper.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v1/params_proto.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v2/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v2/hyper.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v2/partial.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v2/proto.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/src/params_proto/v2/utils.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v1/__init__.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v1/test_hyper.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v1/test_params_proto.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_Eval.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_neo_hyper.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_neo_proto.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_neo_proto_cli.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_neo_proto_partial.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v2/test_utils.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/samples/train.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_advanced_types.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_class_level_methods.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_help_strings.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_method_self_param.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_parse_env_template.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_positional_example.sh +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_comments.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_core.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_envvar.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_linebreaking.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_partial.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_proto_required.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_strings.py +0 -0
- {params_proto-3.1.2 → params_proto-3.2.1}/tests/test_v3/test_sweep.py +0 -0
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
<component name="HighlightingSettingsPerFile">
|
|
34
34
|
<setting file="file://$USER_HOME$/Library/Caches/JetBrains/PyCharm2025.2/python_stubs/-1979844046/_typing.py" root0="SKIP_INSPECTION" />
|
|
35
35
|
<setting file="file://$PROJECT_DIR$/pyproject.toml" root0="SKIP_INSPECTION" />
|
|
36
|
+
<setting file="file://$PROJECT_DIR$/src/params_proto/envvar.py" root0="SKIP_INSPECTION" />
|
|
36
37
|
</component>
|
|
37
38
|
<component name="KubernetesApiPersistence">{}</component>
|
|
38
39
|
<component name="KubernetesApiProvider">{
|
|
@@ -193,10 +194,10 @@
|
|
|
193
194
|
<option name="ADD_CONTENT_ROOTS" value="true" />
|
|
194
195
|
<option name="ADD_SOURCE_ROOTS" value="true" />
|
|
195
196
|
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
|
196
|
-
<option name="RUN_TOOL" value="" />
|
|
197
|
+
<option name="RUN_TOOL" value="true" />
|
|
197
198
|
<option name="_new_keywords" value="""" />
|
|
198
199
|
<option name="_new_parameters" value="""" />
|
|
199
|
-
<option name="_new_additionalArguments" value=""
|
|
200
|
+
<option name="_new_additionalArguments" value=""-n auto"" />
|
|
200
201
|
<option name="_new_target" value=""$PROJECT_DIR$/tests"" />
|
|
201
202
|
<option name="_new_targetType" value=""PATH"" />
|
|
202
203
|
<method v="2" />
|
|
@@ -223,8 +224,8 @@
|
|
|
223
224
|
<component name="SharedIndexes">
|
|
224
225
|
<attachedChunks>
|
|
225
226
|
<set>
|
|
226
|
-
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.
|
|
227
|
-
<option value="bundled-python-sdk-
|
|
227
|
+
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.30387.127" />
|
|
228
|
+
<option value="bundled-python-sdk-3944b0c99280-6d6dccd035ac-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.30387.127" />
|
|
228
229
|
</set>
|
|
229
230
|
</attachedChunks>
|
|
230
231
|
</component>
|
|
@@ -255,6 +256,8 @@
|
|
|
255
256
|
<workItem from="1768126847510" duration="6729000" />
|
|
256
257
|
<workItem from="1768382679094" duration="3000" />
|
|
257
258
|
<workItem from="1769135684123" duration="4247000" />
|
|
259
|
+
<workItem from="1769569263076" duration="1863000" />
|
|
260
|
+
<workItem from="1769933004821" duration="3502000" />
|
|
258
261
|
</task>
|
|
259
262
|
<task id="LOCAL-00001" summary="add design specs">
|
|
260
263
|
<option name="closed" value="true" />
|
|
@@ -295,7 +298,7 @@
|
|
|
295
298
|
<SUITE FILE_PATH="coverage/params_proto$scratch.coverage" NAME="scratch Coverage Results" MODIFIED="1762422882594" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
296
299
|
<SUITE FILE_PATH="coverage/params_proto$demo_params_proto.coverage" NAME="demo_params_proto Coverage Results" MODIFIED="1762230413342" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/scratch" />
|
|
297
300
|
<SUITE FILE_PATH="coverage/params_proto$pytest_for_tests_test_v3_test_proto_cli_test_proto_cli.coverage" NAME="pytest for tests.test_v3.test_proto_cli.test_proto_cli Coverage Results" MODIFIED="1762386112096" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
298
|
-
<SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="
|
|
301
|
+
<SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="1769934711414" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
|
299
302
|
<SUITE FILE_PATH="coverage/params_proto$pytest_in_test_parse_env_template_py.coverage" NAME="pytest in test_parse_env_template.py Coverage Results" MODIFIED="1762242493577" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
300
303
|
<SUITE FILE_PATH="coverage/params_proto$pytest_in_test_v2.coverage" NAME="pytest in test_v2 Coverage Results" MODIFIED="1762232660046" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests/test_v2" />
|
|
301
304
|
<SUITE FILE_PATH="coverage/params_proto$pytest_in_test_neo_proto_py.coverage" NAME="pytest in test_neo_proto.py Coverage Results" MODIFIED="1762233469671" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: params-proto
|
|
3
|
-
Version: 3.1
|
|
3
|
+
Version: 3.2.1
|
|
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
|
|
@@ -36,8 +36,13 @@ class SGD:
|
|
|
36
36
|
def train(optimizer: Adam | SGD, epochs: int = 10):
|
|
37
37
|
pass
|
|
38
38
|
|
|
39
|
-
# CLI: python train.py
|
|
40
|
-
# CLI: python train.py
|
|
39
|
+
# CLI: python train.py adam --lr 0.01 --epochs 50
|
|
40
|
+
# CLI: python train.py sgd --momentum 0.95
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```{note}
|
|
44
|
+
**Version 3.2.0+**: Subcommand attributes are **unprefixed by default**.
|
|
45
|
+
Use `--lr` instead of `--optimizer.lr`. The prefixed syntax still works for backwards compatibility.
|
|
41
46
|
```
|
|
42
47
|
|
|
43
48
|
```python
|
|
@@ -51,15 +56,16 @@ class Model:
|
|
|
51
56
|
def main(model: Model):
|
|
52
57
|
pass
|
|
53
58
|
|
|
54
|
-
# CLI: python main.py
|
|
55
|
-
# CLI: python main.py model
|
|
59
|
+
# CLI: python main.py model --hidden-size 512
|
|
60
|
+
# CLI: python main.py model --name vit
|
|
56
61
|
```
|
|
57
62
|
|
|
58
63
|
```python
|
|
59
|
-
# Pattern 3: Optional simple parameters
|
|
60
|
-
|
|
64
|
+
# Pattern 3: Optional simple parameters
|
|
65
|
+
from typing import Optional
|
|
66
|
+
|
|
61
67
|
@proto.cli
|
|
62
|
-
def process(checkpoint: str = None, batch_size: int = 32):
|
|
68
|
+
def process(checkpoint: Optional[str] = None, batch_size: int = 32):
|
|
63
69
|
pass
|
|
64
70
|
|
|
65
71
|
# CLI: python process.py --checkpoint model.pt
|
|
@@ -75,17 +81,17 @@ Use Union types when you need to **select which class to instantiate**. This cre
|
|
|
75
81
|
First, let's see how users interact with this from the command line:
|
|
76
82
|
|
|
77
83
|
```bash
|
|
78
|
-
# Positional selection (
|
|
79
|
-
python render.py perspective-camera --
|
|
84
|
+
# Positional selection with unprefixed attrs (v3.2.0+)
|
|
85
|
+
python render.py perspective-camera --fov 45 --aspect 1.77
|
|
80
86
|
|
|
81
|
-
#
|
|
82
|
-
python render.py
|
|
87
|
+
# Alternative class
|
|
88
|
+
python render.py orthographic-camera --scale 2.0
|
|
83
89
|
|
|
84
|
-
# With
|
|
85
|
-
python render.py perspective-camera --
|
|
90
|
+
# With shared parameters
|
|
91
|
+
python render.py perspective-camera --output scene.png
|
|
86
92
|
|
|
87
|
-
#
|
|
88
|
-
python render.py --camera:
|
|
93
|
+
# Prefixed syntax still works (backwards compatible)
|
|
94
|
+
python render.py --camera:perspective-camera --camera.fov 45
|
|
89
95
|
```
|
|
90
96
|
|
|
91
97
|
Now the implementation:
|
|
@@ -121,7 +127,7 @@ if __name__ == "__main__":
|
|
|
121
127
|
**How it works:**
|
|
122
128
|
- `camera` is a **required parameter** with Union type
|
|
123
129
|
- params-proto instantiates the selected class
|
|
124
|
-
-
|
|
130
|
+
- Attributes are unprefixed by default (`--fov`, `--aspect`)
|
|
125
131
|
- Use `isinstance()` to dispatch on the selected type
|
|
126
132
|
|
|
127
133
|
## Optional[T]: Simple Optional Parameters
|
|
@@ -129,17 +135,18 @@ if __name__ == "__main__":
|
|
|
129
135
|
`Optional[T]` is for parameters that **may or may not be provided**:
|
|
130
136
|
|
|
131
137
|
```python
|
|
138
|
+
from typing import Optional
|
|
139
|
+
|
|
132
140
|
@proto.cli
|
|
133
141
|
def train(
|
|
134
|
-
checkpoint: str = None,
|
|
135
|
-
# checkpoint: Optional[str] = None, # ⚠️ Doesn't fully work yet
|
|
142
|
+
checkpoint: Optional[str] = None, # Optional with None default
|
|
136
143
|
epochs: int = 10,
|
|
137
144
|
):
|
|
138
145
|
"""Train model."""
|
|
139
146
|
pass
|
|
140
147
|
```
|
|
141
148
|
|
|
142
|
-
**
|
|
149
|
+
**CLI usage:**
|
|
143
150
|
|
|
144
151
|
```bash
|
|
145
152
|
python train.py --checkpoint model.pt # Provide value
|
|
@@ -147,21 +154,15 @@ python train.py # Omit for None default
|
|
|
147
154
|
python train.py --checkpoint model.pt --epochs 50
|
|
148
155
|
```
|
|
149
156
|
|
|
150
|
-
|
|
151
|
-
**Current limitation:** `Optional[str]`, `Optional[int]`, etc. are not fully supported.
|
|
152
|
-
Use regular parameters with defaults as a workaround:
|
|
157
|
+
Both `Optional[str]` and `str = None` work equivalently:
|
|
153
158
|
|
|
154
159
|
```python
|
|
155
|
-
#
|
|
160
|
+
# Both work
|
|
156
161
|
@proto.cli
|
|
157
|
-
def train(checkpoint: str = None
|
|
158
|
-
pass
|
|
162
|
+
def train(checkpoint: Optional[str] = None): ...
|
|
159
163
|
|
|
160
|
-
# ⚠️ Doesn't work yet
|
|
161
164
|
@proto.cli
|
|
162
|
-
def train(checkpoint:
|
|
163
|
-
pass
|
|
164
|
-
```
|
|
165
|
+
def train(checkpoint: str = None): ...
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
## Key Differences
|
|
@@ -169,8 +170,8 @@ def train(checkpoint: Optional[str] = None, epochs: int = 10):
|
|
|
169
170
|
| Type | Purpose | CLI Syntax | When to Use |
|
|
170
171
|
|------|---------|-----------|-------------|
|
|
171
172
|
| `Union[ClassA, ClassB]` | Choose which class instance | `--param:ClassName` or positional | Multiple configurations (optimizers, models, etc.) |
|
|
172
|
-
| `Optional[str]` | Value may or may not be provided | `--param value` | Optional simple parameters
|
|
173
|
-
| `str
|
|
173
|
+
| `Optional[str]` | Value may or may not be provided | `--param value` | Optional simple parameters |
|
|
174
|
+
| `str = None` | Same as Optional | `--param value` | Alternative syntax for optional parameters |
|
|
174
175
|
|
|
175
176
|
## Syntax Variations
|
|
176
177
|
|
|
@@ -180,10 +181,10 @@ def train(checkpoint: Optional[str] = None, epochs: int = 10):
|
|
|
180
181
|
--camera:PerspectiveCamera # Exact match
|
|
181
182
|
--camera:perspective-camera # kebab-case
|
|
182
183
|
--camera:perspectivecamera # lowercase
|
|
183
|
-
perspective-camera # Positional
|
|
184
|
+
perspective-camera # Positional (recommended)
|
|
184
185
|
```
|
|
185
186
|
|
|
186
|
-
**Attribute overrides** (
|
|
187
|
+
**Attribute overrides** (v3.2.0+: unprefixed by default):
|
|
187
188
|
|
|
188
189
|
```python
|
|
189
190
|
@dataclass
|
|
@@ -191,11 +192,37 @@ class Config:
|
|
|
191
192
|
batch_size: int = 32 # Python: snake_case
|
|
192
193
|
learning_rate: float = 0.001
|
|
193
194
|
|
|
194
|
-
# CLI uses kebab-case
|
|
195
|
+
# CLI uses kebab-case (unprefixed by default)
|
|
196
|
+
--batch-size 64
|
|
197
|
+
--learning-rate 0.01
|
|
198
|
+
|
|
199
|
+
# Prefixed syntax still works
|
|
195
200
|
--config.batch-size 64
|
|
196
201
|
--config.learning-rate 0.01
|
|
197
202
|
```
|
|
198
203
|
|
|
204
|
+
### Using `@proto.prefix` for Required Prefixes
|
|
205
|
+
|
|
206
|
+
If you want to **require** prefixed syntax for a class (e.g., for disambiguation), decorate it with `@proto.prefix`:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
@proto.prefix # Now requires --config.batch-size syntax
|
|
210
|
+
@dataclass
|
|
211
|
+
class Config:
|
|
212
|
+
batch_size: int = 32
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class Model:
|
|
216
|
+
batch_size: int = 16 # Same attr name, no conflict
|
|
217
|
+
|
|
218
|
+
@proto.cli
|
|
219
|
+
def train(config: Config, model: Model):
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
# CLI: Config requires prefix, Model doesn't
|
|
223
|
+
# python train.py config model --config.batch-size 64 --batch-size 32
|
|
224
|
+
```
|
|
225
|
+
|
|
199
226
|
## Examples
|
|
200
227
|
|
|
201
228
|
### Example 1: Optimizer Selection
|
|
@@ -203,9 +230,11 @@ class Config:
|
|
|
203
230
|
Command-line usage:
|
|
204
231
|
|
|
205
232
|
```bash
|
|
206
|
-
# Choose optimizer and override defaults
|
|
207
|
-
python train.py adam --
|
|
208
|
-
python train.py sgd --
|
|
233
|
+
# Choose optimizer and override defaults (unprefixed)
|
|
234
|
+
python train.py adam --lr 0.01
|
|
235
|
+
python train.py sgd --momentum 0.95
|
|
236
|
+
|
|
237
|
+
# Prefixed syntax also works
|
|
209
238
|
python train.py --optimizer:Adam --optimizer.beta1 0.95
|
|
210
239
|
```
|
|
211
240
|
|
|
@@ -237,10 +266,10 @@ def train(optimizer: Adam | SGD): # Union type selector
|
|
|
237
266
|
Command-line usage:
|
|
238
267
|
|
|
239
268
|
```bash
|
|
240
|
-
# Positional class selection
|
|
241
|
-
python connect.py database-config --
|
|
269
|
+
# Positional class selection with unprefixed attrs
|
|
270
|
+
python connect.py database-config --host prod.example.com --port 3306
|
|
242
271
|
|
|
243
|
-
# Or named selection
|
|
272
|
+
# Or named selection with prefixed attrs
|
|
244
273
|
python connect.py --db:DatabaseConfig --db.user root
|
|
245
274
|
```
|
|
246
275
|
|
|
@@ -264,11 +293,11 @@ def connect(db: DatabaseConfig): # Single class (still uses Union mechanism)
|
|
|
264
293
|
Command-line usage:
|
|
265
294
|
|
|
266
295
|
```bash
|
|
267
|
-
# Union option + shared parameters
|
|
268
|
-
python render.py perspective-camera --output scene.png --verbose
|
|
296
|
+
# Union option + shared parameters (unprefixed)
|
|
297
|
+
python render.py perspective-camera --fov 45 --output scene.png --verbose
|
|
269
298
|
|
|
270
299
|
# Different union option with overrides
|
|
271
|
-
python render.py
|
|
300
|
+
python render.py orthographic-camera --scale 2.0 --verbose
|
|
272
301
|
|
|
273
302
|
# Help shows all options
|
|
274
303
|
python render.py --help
|
|
@@ -303,6 +332,47 @@ def render(
|
|
|
303
332
|
|
|
304
333
|
**Key point:** Union parameters are **required**, while other parameters can be optional with defaults.
|
|
305
334
|
|
|
335
|
+
### Example 4: @proto.prefix Classes Require Prefixed Syntax
|
|
336
|
+
|
|
337
|
+
When a Union class is decorated with `@proto.prefix`, its CLI attributes require prefixed syntax:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
@proto.prefix # Requires prefixed syntax
|
|
341
|
+
@dataclass
|
|
342
|
+
class Train:
|
|
343
|
+
lr: float = 0.001
|
|
344
|
+
epochs: int = 100
|
|
345
|
+
|
|
346
|
+
@dataclass # Regular dataclass - unprefixed works
|
|
347
|
+
class Evaluate:
|
|
348
|
+
checkpoint: str = "model.pt"
|
|
349
|
+
|
|
350
|
+
@proto.cli
|
|
351
|
+
def main(mode: Train | Evaluate):
|
|
352
|
+
if isinstance(mode, Train):
|
|
353
|
+
print(f"Training: lr={mode.lr}, epochs={mode.epochs}")
|
|
354
|
+
else:
|
|
355
|
+
print(f"Evaluating: {mode.checkpoint}")
|
|
356
|
+
|
|
357
|
+
if __name__ == "__main__":
|
|
358
|
+
main()
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Command-line usage:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Train is @proto.prefix - requires prefixed syntax
|
|
365
|
+
python main.py train --mode.lr 0.01 --mode.epochs 50
|
|
366
|
+
|
|
367
|
+
# Evaluate is regular dataclass - unprefixed works
|
|
368
|
+
python main.py evaluate --checkpoint best.pt
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
This is useful when:
|
|
372
|
+
- You have multiple Union params with overlapping attribute names
|
|
373
|
+
- You want explicit namespacing for clarity
|
|
374
|
+
- You're using the class as a singleton config elsewhere
|
|
375
|
+
|
|
306
376
|
## Related
|
|
307
377
|
|
|
308
378
|
- [Core Concepts](core-concepts) - Three main decorators
|
|
@@ -2,6 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
This page contains the release history and changelog for params-proto.
|
|
4
4
|
|
|
5
|
+
## Version 3.2.1 (2025-02-01)
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **Positional Arguments in Subcommands**: Fixed positional arguments being silently ignored after subcommand name
|
|
10
|
+
- Before: `myapp add my-env/v1.2.3` → positional arg silently dropped
|
|
11
|
+
- After: `myapp add my-env/v1.2.3` → positional arg captured by subcommand's required field
|
|
12
|
+
- Enables CLI patterns like `pip install requests`, `cargo add serde`
|
|
13
|
+
- Raises clear error for extra unrecognized positional arguments
|
|
14
|
+
|
|
15
|
+
### 📚 Documentation
|
|
16
|
+
|
|
17
|
+
- Fixed outdated notes claiming `Optional[str]` was not supported (it works)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Version 3.2.0 (2025-02-01)
|
|
22
|
+
|
|
23
|
+
### ✨ Features
|
|
24
|
+
|
|
25
|
+
- **Unprefixed CLI Subcommand Attributes**: Subcommand attributes no longer require prefix by default
|
|
26
|
+
- Old: `python train.py train-config --config.epochs 200`
|
|
27
|
+
- New: `python train.py train-config --epochs 200`
|
|
28
|
+
- Prefixed syntax still works for backwards compatibility
|
|
29
|
+
|
|
30
|
+
- **`@proto.prefix` Controls CLI Prefix Requirement**: Classes decorated with `@proto.prefix` require prefixed CLI syntax
|
|
31
|
+
- Regular dataclasses: `--epochs 200` (unprefixed)
|
|
32
|
+
- `@proto.prefix` classes: `--config.epochs 200` (prefixed required)
|
|
33
|
+
|
|
34
|
+
### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **`isinstance()` for `@proto.prefix` Instances**: Fixed `isinstance(instance, DecoratedClass)` returning `False`
|
|
37
|
+
- Instances of `@proto.prefix` decorated classes now correctly pass isinstance checks
|
|
38
|
+
- Enables proper type checking in Union subcommand handlers
|
|
39
|
+
|
|
40
|
+
### 🧪 Testing
|
|
41
|
+
|
|
42
|
+
- Added comprehensive nested CLI subcommand tests (`test_nested_cli.py`)
|
|
43
|
+
- Added parallel test execution with `pytest-xdist`
|
|
44
|
+
- Run tests in parallel: `pytest -n auto`
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Version 3.1.2 (2025-01-27)
|
|
49
|
+
|
|
50
|
+
### 📚 Documentation
|
|
51
|
+
|
|
52
|
+
- **Claude Code Plugin**: Fixed install command format in plugin documentation
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
5
56
|
## Version 3.1.1 (2025-01-25)
|
|
6
57
|
|
|
7
58
|
### 🐛 Bug Fixes
|
|
@@ -10,14 +10,14 @@ description: |
|
|
|
10
10
|
(6) Work with Union types for subcommand-like CLI patterns
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# params-proto v3.
|
|
13
|
+
# params-proto v3.2.1
|
|
14
14
|
|
|
15
15
|
Declarative hyperparameter management for ML experiments with automatic CLI generation.
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
pip install params-proto==3.
|
|
20
|
+
pip install params-proto==3.2.0
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## Three Decorators
|
|
@@ -309,9 +309,39 @@ def main(mode: Train | Evaluate):
|
|
|
309
309
|
```
|
|
310
310
|
|
|
311
311
|
```bash
|
|
312
|
-
|
|
312
|
+
# v3.2.0+: Attributes are unprefixed by default
|
|
313
|
+
python main.py train --lr 0.01 --epochs 50
|
|
314
|
+
python main.py evaluate --checkpoint best.pt
|
|
315
|
+
|
|
316
|
+
# Prefixed syntax still works (backwards compatible)
|
|
317
|
+
python main.py --mode:train --mode.lr 0.01
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### @proto.prefix Classes Require Prefixed Syntax
|
|
321
|
+
|
|
322
|
+
When a Union class is decorated with `@proto.prefix`, its attributes require prefixed CLI syntax:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
@proto.prefix # Requires prefixed syntax
|
|
326
|
+
@dataclass
|
|
327
|
+
class Train:
|
|
328
|
+
lr: float = 0.001
|
|
329
|
+
|
|
330
|
+
@dataclass # Regular dataclass - unprefixed works
|
|
331
|
+
class Evaluate:
|
|
332
|
+
checkpoint: str = "model.pt"
|
|
333
|
+
|
|
334
|
+
@proto.cli
|
|
335
|
+
def main(mode: Train | Evaluate):
|
|
336
|
+
...
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
# Train is @proto.prefix - requires prefix
|
|
341
|
+
python main.py train --mode.lr 0.01
|
|
342
|
+
|
|
343
|
+
# Evaluate is regular - unprefixed works
|
|
313
344
|
python main.py evaluate --checkpoint best.pt
|
|
314
|
-
# Also: --mode:train, --mode:Train, --mode:perspective-camera
|
|
315
345
|
```
|
|
316
346
|
|
|
317
347
|
### Enum Types
|
|
Binary file
|
|
@@ -57,6 +57,32 @@ def _normalize_class_name(class_name: str) -> str:
|
|
|
57
57
|
return class_name.replace("-", "").replace("_", "").lower()
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def _get_required_fields(cls) -> List[str]:
|
|
61
|
+
"""Get list of required field names (fields without defaults) in order."""
|
|
62
|
+
import dataclasses
|
|
63
|
+
|
|
64
|
+
required = []
|
|
65
|
+
|
|
66
|
+
# Check if it's a dataclass
|
|
67
|
+
if dataclasses.is_dataclass(cls):
|
|
68
|
+
for field in dataclasses.fields(cls):
|
|
69
|
+
has_default = (
|
|
70
|
+
field.default is not dataclasses.MISSING
|
|
71
|
+
or field.default_factory is not dataclasses.MISSING
|
|
72
|
+
)
|
|
73
|
+
if not has_default:
|
|
74
|
+
required.append(field.name)
|
|
75
|
+
else:
|
|
76
|
+
# For regular classes, check annotations and class-level defaults
|
|
77
|
+
annotations = getattr(cls, "__annotations__", {})
|
|
78
|
+
for name in annotations:
|
|
79
|
+
if not hasattr(cls, name):
|
|
80
|
+
# No class-level default
|
|
81
|
+
required.append(name)
|
|
82
|
+
|
|
83
|
+
return required
|
|
84
|
+
|
|
85
|
+
|
|
60
86
|
def _match_class_by_name(name: str, classes: list) -> Union[type, None]:
|
|
61
87
|
"""Match a string to one of the Union classes.
|
|
62
88
|
|
|
@@ -164,12 +190,31 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
164
190
|
is_bool = annotation == bool
|
|
165
191
|
prefix_params[kebab_key] = (singleton, param_name, annotation, is_bool)
|
|
166
192
|
|
|
193
|
+
# Build unprefixed union attribute map for classes NOT decorated with @proto.prefix
|
|
194
|
+
# Maps attr-name -> (union_param_name, attr_name_underscore)
|
|
195
|
+
# Classes in _SINGLETONS are @proto.prefix decorated and require prefixed attrs
|
|
196
|
+
unprefixed_attrs = {}
|
|
197
|
+
for kebab_name, (param_name, union_classes) in union_params.items():
|
|
198
|
+
for cls in union_classes:
|
|
199
|
+
# Skip if class is a @proto.prefix singleton (requires prefixed attrs)
|
|
200
|
+
is_prefix_class = cls in _SINGLETONS.values()
|
|
201
|
+
if is_prefix_class:
|
|
202
|
+
continue
|
|
203
|
+
if hasattr(cls, "__annotations__"):
|
|
204
|
+
for attr_name in cls.__annotations__:
|
|
205
|
+
kebab_attr = attr_name.replace("_", "-")
|
|
206
|
+
# Map to the union param (first one wins if multiple unions have same attr)
|
|
207
|
+
if kebab_attr not in unprefixed_attrs:
|
|
208
|
+
unprefixed_attrs[kebab_attr] = (param_name, attr_name)
|
|
209
|
+
|
|
167
210
|
# Parse arguments
|
|
168
211
|
result = {}
|
|
169
212
|
prefix_values = {} # (singleton, param_name) -> value
|
|
170
213
|
positional_values = []
|
|
171
214
|
union_selections = {} # param_name -> selected_class
|
|
172
215
|
union_attrs = {} # (param_name, attr_name) -> value
|
|
216
|
+
union_positional = {} # param_name -> [positional_args] for subcommand fields
|
|
217
|
+
current_union_param = None # Track which union we're collecting positionals for
|
|
173
218
|
|
|
174
219
|
args = sys.argv[1:]
|
|
175
220
|
i = 0
|
|
@@ -336,6 +381,17 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
336
381
|
i += 2
|
|
337
382
|
continue
|
|
338
383
|
|
|
384
|
+
# Check unprefixed union attrs when cli_prefix=False
|
|
385
|
+
if key in unprefixed_attrs:
|
|
386
|
+
union_param_name, attr_name = unprefixed_attrs[key]
|
|
387
|
+
# Get the value
|
|
388
|
+
if i + 1 >= len(args):
|
|
389
|
+
raise SystemExit(f"error: argument --{key} requires a value")
|
|
390
|
+
value_str = args[i + 1]
|
|
391
|
+
union_attrs[(union_param_name, attr_name)] = value_str
|
|
392
|
+
i += 2
|
|
393
|
+
continue
|
|
394
|
+
|
|
339
395
|
# Unknown argument
|
|
340
396
|
raise SystemExit(f"error: unrecognized argument: {arg}")
|
|
341
397
|
|
|
@@ -349,12 +405,18 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
349
405
|
selected_class = _match_class_by_name(arg, union_classes)
|
|
350
406
|
if selected_class:
|
|
351
407
|
union_selections[param_name] = selected_class
|
|
408
|
+
current_union_param = param_name # Track for following positionals
|
|
409
|
+
union_positional[param_name] = []
|
|
352
410
|
matched_union = True
|
|
353
411
|
i += 1
|
|
354
412
|
break
|
|
355
413
|
|
|
356
414
|
if not matched_union:
|
|
357
|
-
|
|
415
|
+
# If we have a current union, add positional to its list
|
|
416
|
+
if current_union_param is not None:
|
|
417
|
+
union_positional[current_union_param].append(arg)
|
|
418
|
+
else:
|
|
419
|
+
positional_values.append(arg)
|
|
358
420
|
i += 1
|
|
359
421
|
|
|
360
422
|
# Assign positional arguments to required parameters
|
|
@@ -405,6 +467,33 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
405
467
|
# No annotations, treat as string
|
|
406
468
|
attrs[attr_name] = value_str
|
|
407
469
|
|
|
470
|
+
# Assign positional args to required fields of the selected class
|
|
471
|
+
if param_name in union_positional and union_positional[param_name]:
|
|
472
|
+
positionals = union_positional[param_name]
|
|
473
|
+
required_fields = _get_required_fields(selected_class)
|
|
474
|
+
|
|
475
|
+
for field_idx, field_name in enumerate(required_fields):
|
|
476
|
+
if field_name in attrs:
|
|
477
|
+
# Already set by named arg, skip
|
|
478
|
+
continue
|
|
479
|
+
if field_idx < len(positionals):
|
|
480
|
+
# Get type annotation for conversion
|
|
481
|
+
if hasattr(selected_class, "__annotations__"):
|
|
482
|
+
field_type = selected_class.__annotations__.get(field_name, str)
|
|
483
|
+
try:
|
|
484
|
+
attrs[field_name] = _convert_type(positionals[field_idx], field_type)
|
|
485
|
+
except (ValueError, TypeError):
|
|
486
|
+
raise SystemExit(
|
|
487
|
+
f"error: invalid value for {field_name}: {positionals[field_idx]}"
|
|
488
|
+
)
|
|
489
|
+
else:
|
|
490
|
+
attrs[field_name] = positionals[field_idx]
|
|
491
|
+
|
|
492
|
+
# Check for extra positional args
|
|
493
|
+
if len(positionals) > len(required_fields):
|
|
494
|
+
extra = positionals[len(required_fields):]
|
|
495
|
+
raise SystemExit(f"error: unrecognized arguments: {' '.join(extra)}")
|
|
496
|
+
|
|
408
497
|
# If selected_class is a proto.prefix singleton, merge its overrides
|
|
409
498
|
from params_proto.proto import _SINGLETONS, ptype
|
|
410
499
|
|
|
@@ -419,6 +508,14 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
419
508
|
attrs[key] = value
|
|
420
509
|
break
|
|
421
510
|
|
|
511
|
+
# Check for missing required fields
|
|
512
|
+
required_fields = _get_required_fields(selected_class)
|
|
513
|
+
for field_name in required_fields:
|
|
514
|
+
if field_name not in attrs:
|
|
515
|
+
raise SystemExit(
|
|
516
|
+
f"error: {selected_class.__name__} requires argument: {field_name}"
|
|
517
|
+
)
|
|
518
|
+
|
|
422
519
|
# Instantiate the class with collected attributes
|
|
423
520
|
try:
|
|
424
521
|
instance = selected_class(**attrs)
|