params-proto 3.2.0__tar.gz → 3.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {params_proto-3.2.0 → params_proto-3.2.2}/.claude-plugin/plugin.json +1 -1
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/workspace.xml +5 -9
- {params_proto-3.2.0 → params_proto-3.2.2}/PKG-INFO +1 -2
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/union_types.md +14 -18
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/release_notes.md +31 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/pyproject.toml +1 -2
- params_proto-3.2.2/scratch/test_super.py +146 -0
- params_proto-3.2.2/scratch/test_super_minimal.py +53 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto/SKILL.md +1 -1
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto.skill +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/cli/cli_parse.py +70 -1
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/proto.py +13 -5
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v1/params_proto.py +7 -1
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v2/proto.py +7 -1
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_nested_cli.py +138 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.claude-plugin/marketplace.json +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.editorconfig +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.gitignore +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/.gitignore +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/codeStyles/codeStyleConfig.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/markdown.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/misc.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/modules.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/params-proto.iml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/ruff.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.idea/vcs.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.readthedocs.yaml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.run/pytest for test_neo_proto_cli.run.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/.run/pytest in test_params_proto.run.xml +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/ANSI_HELP_CONSIDERATIONS.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/CLAUDE.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/LICENSE.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/Makefile +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/README +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/README.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/Makefile +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/api/hyper.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/api/proto.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/api/utils.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/advanced_features.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/basic_usage.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/environment_variables.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/hyperparameter_sweeps.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/index.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/examples/nested_configs.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_archive_v2/quick_start.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_static/ansi.css +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/_static/custom.css +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/api/index.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/api/proto.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/conf.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/examples/basic_usage.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/examples/cli_applications.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/examples/ml_training.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/examples/rl_agent.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/index.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/advanced_patterns.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/ansi_formatting.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/cli-fundamentals.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/cli-patterns.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/configuration-patterns.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/core-concepts.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/environment_variables.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/help-generation.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/hyperparameter_sweeps.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/naming-conventions.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/parameter-iteration.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/parameter-overrides.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/type-system.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/key_concepts/welcome.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/migration.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/quick_start.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/docs/requirements.txt +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/examples/union_subcommands.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/figures/man-page.png +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/figures/params-proto-autocompletion.gif +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/figures/spec_files.png +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/scratch/demo_params_proto.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/scratch/demo_v3.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/scratch/proto_DAT_scratch.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/scratch/proto_dependency_tree_pattern.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto/references/cli-and-types.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto/references/environment-vars.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto/references/patterns.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/skills/params-proto/references/sweeps.md +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/app.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/cli/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/cli/ansi_help.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/cli/help_gen.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/documentation.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/envvar.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/hyper/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/hyper/proxies.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/hyper/sweep.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/parse_env_template.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/type_utils.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v1/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v1/hyper.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v2/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v2/hyper.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v2/partial.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/src/params_proto/v2/utils.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v1/__init__.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v1/test_hyper.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v1/test_params_proto.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_Eval.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_neo_hyper.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_neo_proto.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_neo_proto_cli.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_neo_proto_partial.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v2/test_utils.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/conftest.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/samples/train.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_advanced_types.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_class_level_methods.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_cli_parsing.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_help_strings.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_method_self_param.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_parse_env_template.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_piter.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_positional_example.sh +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_comments.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_core.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_envvar.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_linebreaking.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_partial.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_proto_required.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_strings.py +0 -0
- {params_proto-3.2.0 → params_proto-3.2.2}/tests/test_v3/test_sweep.py +0 -0
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
5
|
</component>
|
|
6
6
|
<component name="ChangeListManager">
|
|
7
|
-
<list default="true" id="7a053ece-f497-4c97-ac58-a86c807155ac" name="Changes" comment="add design specs"
|
|
8
|
-
<change beforePath="$PROJECT_DIR$/src/params_proto/cli/cli_parse.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/params_proto/cli/cli_parse.py" afterDir="false" />
|
|
9
|
-
<change beforePath="$PROJECT_DIR$/src/params_proto/hyper/sweep.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/params_proto/hyper/sweep.py" afterDir="false" />
|
|
10
|
-
<change beforePath="$PROJECT_DIR$/src/params_proto/proto.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/params_proto/proto.py" afterDir="false" />
|
|
11
|
-
<change beforePath="$PROJECT_DIR$/tests/test_v3/test_cli_parsing.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_v3/test_cli_parsing.py" afterDir="false" />
|
|
12
|
-
<change beforePath="$PROJECT_DIR$/tests/test_v3/test_piter.py" beforeDir="false" afterPath="$PROJECT_DIR$/tests/test_v3/test_piter.py" afterDir="false" />
|
|
13
|
-
</list>
|
|
7
|
+
<list default="true" id="7a053ece-f497-4c97-ac58-a86c807155ac" name="Changes" comment="add design specs" />
|
|
14
8
|
<option name="SHOW_DIALOG" value="false" />
|
|
15
9
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
16
10
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
@@ -263,7 +257,9 @@
|
|
|
263
257
|
<workItem from="1768382679094" duration="3000" />
|
|
264
258
|
<workItem from="1769135684123" duration="4247000" />
|
|
265
259
|
<workItem from="1769569263076" duration="1863000" />
|
|
266
|
-
<workItem from="1769933004821" duration="
|
|
260
|
+
<workItem from="1769933004821" duration="4284000" />
|
|
261
|
+
<workItem from="1770101044881" duration="1209000" />
|
|
262
|
+
<workItem from="1770110071925" duration="622000" />
|
|
267
263
|
</task>
|
|
268
264
|
<task id="LOCAL-00001" summary="add design specs">
|
|
269
265
|
<option name="closed" value="true" />
|
|
@@ -304,7 +300,7 @@
|
|
|
304
300
|
<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$" />
|
|
305
301
|
<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" />
|
|
306
302
|
<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$" />
|
|
307
|
-
<SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="
|
|
303
|
+
<SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="1769937166592" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
|
|
308
304
|
<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$" />
|
|
309
305
|
<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" />
|
|
310
306
|
<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.2.
|
|
3
|
+
Version: 3.2.2
|
|
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
|
|
@@ -23,7 +23,6 @@ Requires-Dist: argcomplete
|
|
|
23
23
|
Requires-Dist: argparse
|
|
24
24
|
Requires-Dist: expandvars
|
|
25
25
|
Requires-Dist: termcolor
|
|
26
|
-
Requires-Dist: waterbear>=2.6.8
|
|
27
26
|
Provides-Extra: dev
|
|
28
27
|
Requires-Dist: pandas; extra == 'dev'
|
|
29
28
|
Requires-Dist: pytest; extra == 'dev'
|
|
@@ -61,10 +61,11 @@ def main(model: Model):
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
```python
|
|
64
|
-
# Pattern 3: Optional simple parameters
|
|
65
|
-
|
|
64
|
+
# Pattern 3: Optional simple parameters
|
|
65
|
+
from typing import Optional
|
|
66
|
+
|
|
66
67
|
@proto.cli
|
|
67
|
-
def process(checkpoint: str = None, batch_size: int = 32):
|
|
68
|
+
def process(checkpoint: Optional[str] = None, batch_size: int = 32):
|
|
68
69
|
pass
|
|
69
70
|
|
|
70
71
|
# CLI: python process.py --checkpoint model.pt
|
|
@@ -134,17 +135,18 @@ if __name__ == "__main__":
|
|
|
134
135
|
`Optional[T]` is for parameters that **may or may not be provided**:
|
|
135
136
|
|
|
136
137
|
```python
|
|
138
|
+
from typing import Optional
|
|
139
|
+
|
|
137
140
|
@proto.cli
|
|
138
141
|
def train(
|
|
139
|
-
checkpoint: str = None,
|
|
140
|
-
# checkpoint: Optional[str] = None, # ⚠️ Doesn't fully work yet
|
|
142
|
+
checkpoint: Optional[str] = None, # Optional with None default
|
|
141
143
|
epochs: int = 10,
|
|
142
144
|
):
|
|
143
145
|
"""Train model."""
|
|
144
146
|
pass
|
|
145
147
|
```
|
|
146
148
|
|
|
147
|
-
**
|
|
149
|
+
**CLI usage:**
|
|
148
150
|
|
|
149
151
|
```bash
|
|
150
152
|
python train.py --checkpoint model.pt # Provide value
|
|
@@ -152,21 +154,15 @@ python train.py # Omit for None default
|
|
|
152
154
|
python train.py --checkpoint model.pt --epochs 50
|
|
153
155
|
```
|
|
154
156
|
|
|
155
|
-
|
|
156
|
-
**Current limitation:** `Optional[str]`, `Optional[int]`, etc. are not fully supported.
|
|
157
|
-
Use regular parameters with defaults as a workaround:
|
|
157
|
+
Both `Optional[str]` and `str = None` work equivalently:
|
|
158
158
|
|
|
159
159
|
```python
|
|
160
|
-
#
|
|
160
|
+
# Both work
|
|
161
161
|
@proto.cli
|
|
162
|
-
def train(checkpoint: str = None
|
|
163
|
-
pass
|
|
162
|
+
def train(checkpoint: Optional[str] = None): ...
|
|
164
163
|
|
|
165
|
-
# ⚠️ Doesn't work yet
|
|
166
164
|
@proto.cli
|
|
167
|
-
def train(checkpoint:
|
|
168
|
-
pass
|
|
169
|
-
```
|
|
165
|
+
def train(checkpoint: str = None): ...
|
|
170
166
|
```
|
|
171
167
|
|
|
172
168
|
## Key Differences
|
|
@@ -174,8 +170,8 @@ def train(checkpoint: Optional[str] = None, epochs: int = 10):
|
|
|
174
170
|
| Type | Purpose | CLI Syntax | When to Use |
|
|
175
171
|
|------|---------|-----------|-------------|
|
|
176
172
|
| `Union[ClassA, ClassB]` | Choose which class instance | `--param:ClassName` or positional | Multiple configurations (optimizers, models, etc.) |
|
|
177
|
-
| `Optional[str]` | Value may or may not be provided | `--param value` | Optional simple parameters
|
|
178
|
-
| `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 |
|
|
179
175
|
|
|
180
176
|
## Syntax Variations
|
|
181
177
|
|
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
This page contains the release history and changelog for params-proto.
|
|
4
4
|
|
|
5
|
+
## Version 3.2.2 (2025-02-03)
|
|
6
|
+
|
|
7
|
+
### 🐛 Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **`super()` in `@proto` Methods**: Fixed `super()` calls failing with `TypeError: super(type, obj): obj must be an instance or subtype of type`
|
|
10
|
+
- Methods using `super()` in `@proto` or `@proto.prefix` decorated classes now work correctly
|
|
11
|
+
- Root cause: decorated class was recreated as sibling instead of subclass of original
|
|
12
|
+
- Fix: decorated class is now a proper subclass, preserving the MRO chain
|
|
13
|
+
|
|
14
|
+
- **`@proto` Inheriting from `@proto`**: Fixed `TypeError: duplicate base class ptype` when a `@proto` class inherits from another `@proto` class
|
|
15
|
+
- Nested `@proto` inheritance now works correctly
|
|
16
|
+
- Metaclass merging skips duplicate `ptype` bases
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Version 3.2.1 (2025-02-01)
|
|
21
|
+
|
|
22
|
+
### 🐛 Bug Fixes
|
|
23
|
+
|
|
24
|
+
- **Positional Arguments in Subcommands**: Fixed positional arguments being silently ignored after subcommand name
|
|
25
|
+
- Before: `myapp add my-env/v1.2.3` → positional arg silently dropped
|
|
26
|
+
- After: `myapp add my-env/v1.2.3` → positional arg captured by subcommand's required field
|
|
27
|
+
- Enables CLI patterns like `pip install requests`, `cargo add serde`
|
|
28
|
+
- Raises clear error for extra unrecognized positional arguments
|
|
29
|
+
|
|
30
|
+
### 📚 Documentation
|
|
31
|
+
|
|
32
|
+
- Fixed outdated notes claiming `Optional[str]` was not supported (it works)
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
5
36
|
## Version 3.2.0 (2025-02-01)
|
|
6
37
|
|
|
7
38
|
### ✨ Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "params-proto"
|
|
3
|
-
version = "3.2.
|
|
3
|
+
version = "3.2.2"
|
|
4
4
|
description = "Modern Hyper Parameter Management for Machine Learning"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Ge Yang" }
|
|
@@ -32,7 +32,6 @@ classifiers = [
|
|
|
32
32
|
]
|
|
33
33
|
|
|
34
34
|
dependencies = [
|
|
35
|
-
"waterbear>=2.6.8",
|
|
36
35
|
"argparse",
|
|
37
36
|
"argcomplete",
|
|
38
37
|
"expandvars",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Test super() calls inside @proto and @proto.prefix decorated classes."""
|
|
2
|
+
|
|
3
|
+
from params_proto import proto
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Test 1: Basic inheritance with @proto
|
|
7
|
+
class BaseClass:
|
|
8
|
+
base_value: int = 10
|
|
9
|
+
|
|
10
|
+
def base_method(self):
|
|
11
|
+
return f"base: {self.base_value}"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@proto
|
|
15
|
+
class ChildProto(BaseClass):
|
|
16
|
+
child_value: int = 20
|
|
17
|
+
|
|
18
|
+
def child_method(self):
|
|
19
|
+
base_result = super().base_method()
|
|
20
|
+
return f"{base_result}, child: {self.child_value}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Test 2: Inheritance with @proto.prefix
|
|
24
|
+
class BaseConfig:
|
|
25
|
+
lr: float = 0.01
|
|
26
|
+
|
|
27
|
+
def describe(self):
|
|
28
|
+
return f"lr={self.lr}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@proto.prefix
|
|
32
|
+
class ChildConfig(BaseConfig):
|
|
33
|
+
batch_size: int = 32
|
|
34
|
+
|
|
35
|
+
def describe(self):
|
|
36
|
+
base = super().describe()
|
|
37
|
+
return f"{base}, batch_size={self.batch_size}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Test 3: Inheritance between two @proto decorated classes
|
|
41
|
+
@proto
|
|
42
|
+
class ParentProto:
|
|
43
|
+
parent_attr: str = "parent"
|
|
44
|
+
|
|
45
|
+
def get_info(self):
|
|
46
|
+
return f"Parent: {self.parent_attr}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@proto
|
|
50
|
+
class GrandchildProto(ParentProto):
|
|
51
|
+
grandchild_attr: str = "grandchild"
|
|
52
|
+
|
|
53
|
+
def get_info(self):
|
|
54
|
+
parent_info = super().get_info()
|
|
55
|
+
return f"{parent_info}, Grandchild: {self.grandchild_attr}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Test 4: __post_init__ with super() (undecorated base)
|
|
59
|
+
class BaseWithInit:
|
|
60
|
+
value: int = 1
|
|
61
|
+
processed: bool = False
|
|
62
|
+
|
|
63
|
+
def __post_init__(self):
|
|
64
|
+
self.processed = True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@proto
|
|
68
|
+
class ChildWithInit(BaseWithInit):
|
|
69
|
+
multiplier: int = 2
|
|
70
|
+
child_processed: bool = False
|
|
71
|
+
|
|
72
|
+
def __post_init__(self):
|
|
73
|
+
super().__post_init__()
|
|
74
|
+
self.child_processed = True
|
|
75
|
+
self.value *= self.multiplier
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main():
|
|
79
|
+
print("=" * 60)
|
|
80
|
+
print("Testing super() in @proto and @proto.prefix classes")
|
|
81
|
+
print("=" * 60)
|
|
82
|
+
|
|
83
|
+
# Test 1
|
|
84
|
+
print("\n--- Test 1: Basic inheritance with @proto ---")
|
|
85
|
+
try:
|
|
86
|
+
child = ChildProto()
|
|
87
|
+
print(f"child class: {child.__class__}")
|
|
88
|
+
print(f"child MRO: {child.__class__.__mro__}")
|
|
89
|
+
print(f"ChildProto: {ChildProto}")
|
|
90
|
+
print(f"ChildProto MRO: {ChildProto.__mro__}")
|
|
91
|
+
print(f"isinstance(child, ChildProto): {isinstance(child, ChildProto)}")
|
|
92
|
+
print(f"isinstance(child, BaseClass): {isinstance(child, BaseClass)}")
|
|
93
|
+
result = child.child_method()
|
|
94
|
+
print(f"Result: {result}")
|
|
95
|
+
assert "base: 10" in result, f"Expected 'base: 10' in result"
|
|
96
|
+
assert "child: 20" in result, f"Expected 'child: 20' in result"
|
|
97
|
+
print("✓ PASSED")
|
|
98
|
+
except Exception as e:
|
|
99
|
+
import traceback
|
|
100
|
+
print(f"✗ FAILED: {e}")
|
|
101
|
+
traceback.print_exc()
|
|
102
|
+
|
|
103
|
+
# Test 2
|
|
104
|
+
print("\n--- Test 2: Inheritance with @proto.prefix ---")
|
|
105
|
+
try:
|
|
106
|
+
config = ChildConfig()
|
|
107
|
+
result = config.describe()
|
|
108
|
+
print(f"Result: {result}")
|
|
109
|
+
assert "lr=" in result, f"Expected 'lr=' in result"
|
|
110
|
+
assert "batch_size=" in result, f"Expected 'batch_size=' in result"
|
|
111
|
+
print("✓ PASSED")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"✗ FAILED: {e}")
|
|
114
|
+
|
|
115
|
+
# Test 3
|
|
116
|
+
print("\n--- Test 3: Inheritance between @proto classes ---")
|
|
117
|
+
try:
|
|
118
|
+
grandchild = GrandchildProto()
|
|
119
|
+
result = grandchild.get_info()
|
|
120
|
+
print(f"Result: {result}")
|
|
121
|
+
assert "Parent:" in result, f"Expected 'Parent:' in result"
|
|
122
|
+
assert "Grandchild:" in result, f"Expected 'Grandchild:' in result"
|
|
123
|
+
print("✓ PASSED")
|
|
124
|
+
except Exception as e:
|
|
125
|
+
import traceback
|
|
126
|
+
print(f"✗ FAILED: {e}")
|
|
127
|
+
traceback.print_exc()
|
|
128
|
+
|
|
129
|
+
# Test 4
|
|
130
|
+
print("\n--- Test 4: __post_init__ with super() ---")
|
|
131
|
+
try:
|
|
132
|
+
obj = ChildWithInit()
|
|
133
|
+
print(f"value={obj.value}, processed={obj.processed}, child_processed={obj.child_processed}")
|
|
134
|
+
assert obj.processed, "Expected processed=True from parent __post_init__"
|
|
135
|
+
assert obj.child_processed, "Expected child_processed=True from child __post_init__"
|
|
136
|
+
assert obj.value == 2, f"Expected value=2 (1*2), got {obj.value}"
|
|
137
|
+
print("✓ PASSED")
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"✗ FAILED: {e}")
|
|
140
|
+
|
|
141
|
+
print("\n" + "=" * 60)
|
|
142
|
+
print("All tests completed!")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Minimal test for super() issue."""
|
|
2
|
+
|
|
3
|
+
from params_proto import proto
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Base:
|
|
7
|
+
def method(self):
|
|
8
|
+
return "base"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@proto
|
|
12
|
+
class Child(Base):
|
|
13
|
+
attr: int = 1
|
|
14
|
+
|
|
15
|
+
def method(self):
|
|
16
|
+
# This should call Base.method()
|
|
17
|
+
return f"child + {super().method()}"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Test without instantiation
|
|
21
|
+
print("Child class:", Child)
|
|
22
|
+
print("Child id:", id(Child))
|
|
23
|
+
print("Child.__mro__:", Child.__mro__)
|
|
24
|
+
print("Child.__proto_original_class__:", Child.__proto_original_class__)
|
|
25
|
+
print("Original class id:", id(Child.__proto_original_class__))
|
|
26
|
+
print("Same class?", Child is Child.__proto_original_class__)
|
|
27
|
+
|
|
28
|
+
# Test instantiation
|
|
29
|
+
print("\nCreating instance...")
|
|
30
|
+
child = Child()
|
|
31
|
+
print("Instance class:", child.__class__)
|
|
32
|
+
print("Instance type:", type(child))
|
|
33
|
+
|
|
34
|
+
# Check what method is on the instance
|
|
35
|
+
print("\nMethod on instance:", child.method)
|
|
36
|
+
|
|
37
|
+
# Try calling
|
|
38
|
+
print("\nCalling method...")
|
|
39
|
+
try:
|
|
40
|
+
result = child.method()
|
|
41
|
+
print(f"Result: {result}")
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Error: {e}")
|
|
44
|
+
|
|
45
|
+
# Debug: manually call the original method
|
|
46
|
+
print("\nDebug: Trying to call original method directly...")
|
|
47
|
+
original_cls = Child.__proto_original_class__
|
|
48
|
+
print(f"Original class: {original_cls}")
|
|
49
|
+
print(f"Original method: {original_cls.method}")
|
|
50
|
+
|
|
51
|
+
# Create instance via original class
|
|
52
|
+
from params_proto.proto import _SINGLETONS
|
|
53
|
+
print(f"\nSINGLETONS: {_SINGLETONS}")
|
|
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
|
|
|
@@ -187,6 +213,8 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
187
213
|
positional_values = []
|
|
188
214
|
union_selections = {} # param_name -> selected_class
|
|
189
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
|
|
190
218
|
|
|
191
219
|
args = sys.argv[1:]
|
|
192
220
|
i = 0
|
|
@@ -377,12 +405,18 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
377
405
|
selected_class = _match_class_by_name(arg, union_classes)
|
|
378
406
|
if selected_class:
|
|
379
407
|
union_selections[param_name] = selected_class
|
|
408
|
+
current_union_param = param_name # Track for following positionals
|
|
409
|
+
union_positional[param_name] = []
|
|
380
410
|
matched_union = True
|
|
381
411
|
i += 1
|
|
382
412
|
break
|
|
383
413
|
|
|
384
414
|
if not matched_union:
|
|
385
|
-
|
|
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)
|
|
386
420
|
i += 1
|
|
387
421
|
|
|
388
422
|
# Assign positional arguments to required parameters
|
|
@@ -433,6 +467,33 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
433
467
|
# No annotations, treat as string
|
|
434
468
|
attrs[attr_name] = value_str
|
|
435
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
|
+
|
|
436
497
|
# If selected_class is a proto.prefix singleton, merge its overrides
|
|
437
498
|
from params_proto.proto import _SINGLETONS, ptype
|
|
438
499
|
|
|
@@ -447,6 +508,14 @@ def parse_cli_args(wrapper) -> Dict[str, Any]:
|
|
|
447
508
|
attrs[key] = value
|
|
448
509
|
break
|
|
449
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
|
+
|
|
450
519
|
# Instantiate the class with collected attributes
|
|
451
520
|
try:
|
|
452
521
|
instance = selected_class(**attrs)
|
|
@@ -844,10 +844,15 @@ def proto(
|
|
|
844
844
|
# Handle existing metaclass
|
|
845
845
|
existing_meta = type(obj)
|
|
846
846
|
if existing_meta is not type:
|
|
847
|
-
#
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
847
|
+
# Check if existing metaclass is already ptype or a subclass of ptype
|
|
848
|
+
if issubclass(existing_meta, ptype):
|
|
849
|
+
# Already using ptype, no need to merge
|
|
850
|
+
metaclass = existing_meta
|
|
851
|
+
else:
|
|
852
|
+
# Merge with existing metaclass
|
|
853
|
+
class MergedMeta(existing_meta, ptype):
|
|
854
|
+
pass
|
|
855
|
+
metaclass = MergedMeta
|
|
851
856
|
else:
|
|
852
857
|
metaclass = ptype
|
|
853
858
|
|
|
@@ -872,9 +877,12 @@ def proto(
|
|
|
872
877
|
namespace[key] = value
|
|
873
878
|
|
|
874
879
|
# Create new class with metaclass
|
|
880
|
+
# IMPORTANT: Use (obj,) as bases to make new class a SUBCLASS of original.
|
|
881
|
+
# This ensures super() works correctly - the original class is in the MRO,
|
|
882
|
+
# so Python's super() validation passes when checking isinstance(self, original_class).
|
|
875
883
|
new_cls = metaclass(
|
|
876
884
|
obj.__name__,
|
|
877
|
-
obj
|
|
885
|
+
(obj,),
|
|
878
886
|
namespace
|
|
879
887
|
)
|
|
880
888
|
|
|
@@ -17,7 +17,13 @@ import inspect
|
|
|
17
17
|
import re
|
|
18
18
|
from typing import Callable, TypeVar, Union
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
try:
|
|
21
|
+
from waterbear import DefaultBear
|
|
22
|
+
except ImportError:
|
|
23
|
+
raise ImportError(
|
|
24
|
+
"params_proto.v1 requires waterbear. Install it with: pip install waterbear\n"
|
|
25
|
+
"Note: The v3 API (from params_proto import proto) does not require waterbear."
|
|
26
|
+
)
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
def _strtobool(val):
|
|
@@ -8,7 +8,13 @@ from types import BuiltinFunctionType, SimpleNamespace
|
|
|
8
8
|
from warnings import warn
|
|
9
9
|
|
|
10
10
|
from expandvars import expandvars
|
|
11
|
-
|
|
11
|
+
try:
|
|
12
|
+
from waterbear import Bear
|
|
13
|
+
except ImportError:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
"params_proto.v2 requires waterbear. Install it with: pip install waterbear\n"
|
|
16
|
+
"Note: The v3 API (from params_proto import proto) does not require waterbear."
|
|
17
|
+
)
|
|
12
18
|
|
|
13
19
|
from params_proto.parse_env_template import all_available
|
|
14
20
|
from params_proto.v2.utils import dot_to_deps
|
|
@@ -816,3 +816,141 @@ class TestProtoPrefixRequiresPrefix:
|
|
|
816
816
|
# Unprefixed should error for @proto.prefix class
|
|
817
817
|
result = run_cli(script, ["train-config", "--epochs", "200"], expect_error=True)
|
|
818
818
|
assert "unrecognized argument" in result["stderr"]
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
class TestPositionalArgsInSubcommands:
|
|
822
|
+
"""Test positional arguments captured by subcommand fields.
|
|
823
|
+
|
|
824
|
+
This tests the pattern: myapp add my-env/v1.2.3
|
|
825
|
+
Where 'add' is the subcommand and 'my-env/v1.2.3' is captured by the
|
|
826
|
+
subcommand's first required field.
|
|
827
|
+
"""
|
|
828
|
+
|
|
829
|
+
def test_positional_arg_captured_by_subcommand_field(self, run_cli):
|
|
830
|
+
"""Test positional arg after subcommand name is captured."""
|
|
831
|
+
script = dedent("""
|
|
832
|
+
from dataclasses import dataclass
|
|
833
|
+
from params_proto import proto
|
|
834
|
+
|
|
835
|
+
@dataclass
|
|
836
|
+
class AddCommand:
|
|
837
|
+
env: str # Required field, no default
|
|
838
|
+
|
|
839
|
+
@dataclass
|
|
840
|
+
class RemoveCommand:
|
|
841
|
+
env: str # Required field
|
|
842
|
+
|
|
843
|
+
@proto.cli
|
|
844
|
+
def main(command: AddCommand | RemoveCommand):
|
|
845
|
+
print(f"command={command.__class__.__name__}")
|
|
846
|
+
print(f"env={command.env}")
|
|
847
|
+
|
|
848
|
+
if __name__ == "__main__":
|
|
849
|
+
main()
|
|
850
|
+
""")
|
|
851
|
+
|
|
852
|
+
# Positional arg after subcommand should be captured by 'env' field
|
|
853
|
+
result = run_cli(script, ["add", "my-env/v1.2.3"])
|
|
854
|
+
lines = result["stdout"].strip().split("\n")
|
|
855
|
+
assert lines[0] == "command=AddCommand"
|
|
856
|
+
assert lines[1] == "env=my-env/v1.2.3"
|
|
857
|
+
|
|
858
|
+
result = run_cli(script, ["remove", "old-env/v0.9.0"])
|
|
859
|
+
lines = result["stdout"].strip().split("\n")
|
|
860
|
+
assert lines[0] == "command=RemoveCommand"
|
|
861
|
+
assert lines[1] == "env=old-env/v0.9.0"
|
|
862
|
+
|
|
863
|
+
def test_multiple_positional_args_in_subcommand(self, run_cli):
|
|
864
|
+
"""Test multiple positional args captured by subcommand fields."""
|
|
865
|
+
script = dedent("""
|
|
866
|
+
from dataclasses import dataclass
|
|
867
|
+
from params_proto import proto
|
|
868
|
+
|
|
869
|
+
@dataclass
|
|
870
|
+
class InstallCommand:
|
|
871
|
+
package: str # Required, first positional
|
|
872
|
+
version: str # Required, second positional
|
|
873
|
+
|
|
874
|
+
@proto.cli
|
|
875
|
+
def main(command: InstallCommand):
|
|
876
|
+
print(f"package={command.package}")
|
|
877
|
+
print(f"version={command.version}")
|
|
878
|
+
|
|
879
|
+
if __name__ == "__main__":
|
|
880
|
+
main()
|
|
881
|
+
""")
|
|
882
|
+
|
|
883
|
+
result = run_cli(script, ["install", "requests", "2.28.0"])
|
|
884
|
+
lines = result["stdout"].strip().split("\n")
|
|
885
|
+
assert lines[0] == "package=requests"
|
|
886
|
+
assert lines[1] == "version=2.28.0"
|
|
887
|
+
|
|
888
|
+
def test_positional_with_named_args(self, run_cli):
|
|
889
|
+
"""Test mixing positional and named args in subcommand."""
|
|
890
|
+
script = dedent("""
|
|
891
|
+
from dataclasses import dataclass
|
|
892
|
+
from params_proto import proto
|
|
893
|
+
|
|
894
|
+
@dataclass
|
|
895
|
+
class CloneCommand:
|
|
896
|
+
url: str # Required positional
|
|
897
|
+
depth: int = 0 # Optional with default
|
|
898
|
+
|
|
899
|
+
@proto.cli
|
|
900
|
+
def main(command: CloneCommand):
|
|
901
|
+
print(f"url={command.url}")
|
|
902
|
+
print(f"depth={command.depth}")
|
|
903
|
+
|
|
904
|
+
if __name__ == "__main__":
|
|
905
|
+
main()
|
|
906
|
+
""")
|
|
907
|
+
|
|
908
|
+
# Positional url, named depth
|
|
909
|
+
result = run_cli(script, ["clone", "https://github.com/test", "--depth", "1"])
|
|
910
|
+
lines = result["stdout"].strip().split("\n")
|
|
911
|
+
assert lines[0] == "url=https://github.com/test"
|
|
912
|
+
assert lines[1] == "depth=1"
|
|
913
|
+
|
|
914
|
+
def test_positional_arg_missing_should_error(self, run_cli):
|
|
915
|
+
"""Test that missing required positional arg errors."""
|
|
916
|
+
script = dedent("""
|
|
917
|
+
from dataclasses import dataclass
|
|
918
|
+
from params_proto import proto
|
|
919
|
+
|
|
920
|
+
@dataclass
|
|
921
|
+
class AddCommand:
|
|
922
|
+
env: str # Required field
|
|
923
|
+
|
|
924
|
+
@proto.cli
|
|
925
|
+
def main(command: AddCommand):
|
|
926
|
+
print(f"env={command.env}")
|
|
927
|
+
|
|
928
|
+
if __name__ == "__main__":
|
|
929
|
+
main()
|
|
930
|
+
""")
|
|
931
|
+
|
|
932
|
+
# Missing required positional should error
|
|
933
|
+
result = run_cli(script, ["add"], expect_error=True)
|
|
934
|
+
assert result["returncode"] != 0
|
|
935
|
+
|
|
936
|
+
def test_extra_positional_should_error(self, run_cli):
|
|
937
|
+
"""Test that extra unrecognized positional args error."""
|
|
938
|
+
script = dedent("""
|
|
939
|
+
from dataclasses import dataclass
|
|
940
|
+
from params_proto import proto
|
|
941
|
+
|
|
942
|
+
@dataclass
|
|
943
|
+
class AddCommand:
|
|
944
|
+
env: str # Required field
|
|
945
|
+
|
|
946
|
+
@proto.cli
|
|
947
|
+
def main(command: AddCommand):
|
|
948
|
+
print(f"env={command.env}")
|
|
949
|
+
|
|
950
|
+
if __name__ == "__main__":
|
|
951
|
+
main()
|
|
952
|
+
""")
|
|
953
|
+
|
|
954
|
+
# Extra positional arg should error
|
|
955
|
+
result = run_cli(script, ["add", "env1", "extra-arg"], expect_error=True)
|
|
956
|
+
assert result["returncode"] != 0
|
|
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.0 → params_proto-3.2.2}/docs/_archive_v2/examples/environment_variables.md
RENAMED
|
File without changes
|
{params_proto-3.2.0 → params_proto-3.2.2}/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
|
{params_proto-3.2.0 → params_proto-3.2.2}/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
|