wry 0.1.6.dev3__tar.gz → 0.1.7__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.
- {wry-0.1.6.dev3 → wry-0.1.7}/.github/workflows/ci-cd.yml +16 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/.pre-commit-config.yaml +9 -4
- {wry-0.1.6.dev3 → wry-0.1.7}/CHANGELOG.md +23 -1
- {wry-0.1.6.dev3 → wry-0.1.7}/PKG-INFO +1 -1
- wry-0.1.7/tests/unit/test_generate_click_classmethod.py +47 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/_version.py +3 -3
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/click_integration.py +5 -1
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/model.py +31 -4
- {wry-0.1.6.dev3 → wry-0.1.7}/wry.egg-info/PKG-INFO +1 -1
- {wry-0.1.6.dev3 → wry-0.1.7}/wry.egg-info/SOURCES.txt +1 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/.gitignore +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/.markdownlint.json +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/LICENSE +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/README.md +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/TODO.md +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/check.sh +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/auto_model_example.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/config.json +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/intermediate_example.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/multi_model_example.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/simple_cli.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/examples/source_tracking_example.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/pyproject.toml +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/scripts/README.md +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/scripts/extract_release_notes.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/scripts/test_all_versions.sh +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/scripts/test_ci_locally.sh +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/scripts/test_with_act.sh +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/setup.cfg +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/README.md +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/features/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/features/test_auto_model.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/features/test_multi_model.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/features/test_source_precedence.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/integration/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/integration/test_click_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/integration/test_click_integration.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/integration/test_click_integration_extended.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/integration/test_context_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/test_auto_model_annotation_inference.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/test_auto_model_field_processing.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/test_field_annotation_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/test_field_annotations.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/auto_model/test_type_inference.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/README_TESTING_STRATEGY.md +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_bool_flag_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_config_building.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_constraint_formatting.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_decorator_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_interval_constraints.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_lambda_parsing.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_length_constraints.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_parameter_generation.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_click_predicate_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_closure_extraction_errors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_closure_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_constraint_behavior.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_constraint_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_env_vars_option.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_json_config_loading.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_lambda_behavior.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_lambda_error_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_predicate_source_errors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_strict_mode_errors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/click/test_type_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_accessors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_advanced_features.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_core.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_env_utils.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_field_constraint_extraction.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_field_utils.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_field_utils_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_sources.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/core/test_type_checking_blocks.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_accessor_caching.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_extract_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_click_context_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_data_extraction.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_default_handling.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_environment_integration.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_extract_subset_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_extraction_methods.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_field_errors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_model_object_extraction.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_non_dict_object_extraction.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/model/test_object_attribute_extraction.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/multi_model/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/multi_model/test_multi_model.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/multi_model/test_type_checking.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_auto_model_field_processing.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_comprehensive_imports.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_init.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_init_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_init_version_edge_cases.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_model_extraction_methods.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_type_checking_imports.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_version_fallback.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/tests/unit/test_version_parsing.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/auto_model.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/__init__.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/accessors.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/env_utils.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/field_utils.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/core/sources.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/multi_model.py +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry/py.typed +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry.egg-info/dependency_links.txt +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry.egg-info/requires.txt +0 -0
- {wry-0.1.6.dev3 → wry-0.1.7}/wry.egg-info/top_level.txt +0 -0
|
@@ -177,6 +177,14 @@ jobs:
|
|
|
177
177
|
./dist/*.tar.gz
|
|
178
178
|
./dist/*.whl
|
|
179
179
|
|
|
180
|
+
- name: Get previous tag
|
|
181
|
+
id: previous_tag
|
|
182
|
+
run: |
|
|
183
|
+
# Get the previous tag (excluding the current one)
|
|
184
|
+
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v '${{ github.ref_name }}' | head -n 1)
|
|
185
|
+
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
|
|
186
|
+
echo "Previous tag: $PREVIOUS_TAG"
|
|
187
|
+
|
|
180
188
|
- name: Extract release notes from CHANGELOG
|
|
181
189
|
id: release_notes
|
|
182
190
|
run: |
|
|
@@ -186,6 +194,14 @@ jobs:
|
|
|
186
194
|
# Extract release notes and save to file
|
|
187
195
|
python scripts/extract_release_notes.py '${{ github.ref_name }}' > release_notes.md
|
|
188
196
|
|
|
197
|
+
# Add comparison link if we have a previous tag
|
|
198
|
+
if [ -n "${{ steps.previous_tag.outputs.previous_tag }}" ]; then
|
|
199
|
+
echo "" >> release_notes.md
|
|
200
|
+
echo "---" >> release_notes.md
|
|
201
|
+
echo "" >> release_notes.md
|
|
202
|
+
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.previous_tag.outputs.previous_tag }}...${{ github.ref_name }}" >> release_notes.md
|
|
203
|
+
fi
|
|
204
|
+
|
|
189
205
|
# Read the content for the release body
|
|
190
206
|
echo 'RELEASE_NOTES<<EOF' >> $GITHUB_ENV
|
|
191
207
|
cat release_notes.md >> $GITHUB_ENV
|
|
@@ -28,13 +28,18 @@ repos:
|
|
|
28
28
|
args: [--cov=wry, --cov-branch, --cov-report=xml, --cov-report=term-missing, --cov-fail-under=90]
|
|
29
29
|
pass_filenames: false
|
|
30
30
|
always_run: true
|
|
31
|
+
|
|
32
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
33
|
+
rev: v1.17.1
|
|
34
|
+
hooks:
|
|
31
35
|
- id: mypy
|
|
32
|
-
name: mypy
|
|
33
|
-
entry: mypy
|
|
34
|
-
language: system
|
|
35
|
-
types: [python]
|
|
36
36
|
args: [wry/]
|
|
37
37
|
pass_filenames: false
|
|
38
|
+
additional_dependencies:
|
|
39
|
+
- pydantic>=2.9.2
|
|
40
|
+
- click>=8.0.0
|
|
41
|
+
- annotated-types>=0.6.0
|
|
42
|
+
- setuptools-scm>=8.0
|
|
38
43
|
|
|
39
44
|
# Temporarily disabled - bandit has dependency issues in pre-commit
|
|
40
45
|
# - repo: https://github.com/PyCQA/bandit
|
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.7] - 2025-09-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `generate_click_parameters` as a classmethod on `WryModel` and `AutoWryModel`
|
|
15
|
+
- Allows using `@MyModel.generate_click_parameters()` directly without imports
|
|
16
|
+
- Simplifies API and reduces import requirements in downstream projects
|
|
17
|
+
|
|
18
|
+
## [0.1.6] - 2025-09-08
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- GitHub releases now include commit comparison links to previous releases
|
|
23
|
+
- Development versions now use `.devN` format and show as pre-releases on PyPI
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Switched setuptools-scm from `post-release` to `guess-next-dev` version scheme
|
|
28
|
+
- Development versions now appear as pre-releases instead of regular releases on PyPI
|
|
29
|
+
|
|
10
30
|
## [0.1.5] - 2025-09-08
|
|
11
31
|
|
|
12
32
|
### Fixed
|
|
@@ -115,7 +135,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
115
135
|
|
|
116
136
|
- Initial version as `drycli` package (before rename to `wry`)
|
|
117
137
|
|
|
118
|
-
[Unreleased]: https://github.com/tahouse/wry/compare/v0.1.
|
|
138
|
+
[Unreleased]: https://github.com/tahouse/wry/compare/v0.1.7...HEAD
|
|
139
|
+
[0.1.7]: https://github.com/tahouse/wry/compare/v0.1.6...v0.1.7
|
|
140
|
+
[0.1.6]: https://github.com/tahouse/wry/compare/v0.1.5...v0.1.6
|
|
119
141
|
[0.1.5]: https://github.com/tahouse/wry/compare/v0.1.4...v0.1.5
|
|
120
142
|
[0.1.4]: https://github.com/tahouse/wry/compare/v0.1.3...v0.1.4
|
|
121
143
|
[0.1.3]: https://github.com/tahouse/wry/compare/v0.1.2...v0.1.3
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Test generate_click_parameters as a classmethod."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from wry import AutoWryModel, WryModel, generate_click_parameters
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestGenerateClickClassmethod:
|
|
10
|
+
"""Test the generate_click_parameters classmethod."""
|
|
11
|
+
|
|
12
|
+
def test_wry_model_has_classmethod(self):
|
|
13
|
+
"""Test that WryModel has generate_click_parameters classmethod."""
|
|
14
|
+
assert hasattr(WryModel, "generate_click_parameters")
|
|
15
|
+
assert callable(WryModel.generate_click_parameters)
|
|
16
|
+
|
|
17
|
+
def test_auto_wry_model_has_classmethod(self):
|
|
18
|
+
"""Test that AutoWryModel has generate_click_parameters classmethod."""
|
|
19
|
+
assert hasattr(AutoWryModel, "generate_click_parameters")
|
|
20
|
+
assert callable(AutoWryModel.generate_click_parameters)
|
|
21
|
+
|
|
22
|
+
def test_classmethod_returns_same_as_function(self):
|
|
23
|
+
"""Test that classmethod returns same decorator as direct function call."""
|
|
24
|
+
|
|
25
|
+
class SimpleModel(WryModel):
|
|
26
|
+
value: int = Field(default=1)
|
|
27
|
+
|
|
28
|
+
# Get decorator from classmethod
|
|
29
|
+
classmethod_decorator = SimpleModel.generate_click_parameters()
|
|
30
|
+
# Get decorator from function
|
|
31
|
+
function_decorator = generate_click_parameters(SimpleModel)
|
|
32
|
+
|
|
33
|
+
# Both should be callables
|
|
34
|
+
assert callable(classmethod_decorator)
|
|
35
|
+
assert callable(function_decorator)
|
|
36
|
+
|
|
37
|
+
# They should have the same behavior (though not necessarily the same object)
|
|
38
|
+
# The important thing is that the classmethod works
|
|
39
|
+
@click.command()
|
|
40
|
+
@classmethod_decorator
|
|
41
|
+
def dummy(**kwargs):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
# Should have config and show_env_vars options added
|
|
45
|
+
param_names = [p.name for p in dummy.params]
|
|
46
|
+
assert "config" in param_names
|
|
47
|
+
assert "show_env_vars" in param_names
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.7'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 7)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g4e1c76e68'
|
|
@@ -357,7 +357,11 @@ def generate_click_parameters(
|
|
|
357
357
|
field_type = AutoClickParameter.REQUIRED_OPTION
|
|
358
358
|
elif item == AutoClickParameter.ARGUMENT:
|
|
359
359
|
field_type = AutoClickParameter.ARGUMENT
|
|
360
|
-
elif
|
|
360
|
+
elif (
|
|
361
|
+
hasattr(item, "__module__")
|
|
362
|
+
and "click" in str(item.__module__)
|
|
363
|
+
and not isinstance(item, AutoClickParameter)
|
|
364
|
+
):
|
|
361
365
|
click_parameter = item
|
|
362
366
|
break
|
|
363
367
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Core WryModel implementation."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from pathlib import Path
|
|
5
|
-
from typing import Any, ClassVar, TypeVar
|
|
6
|
+
from typing import Any, ClassVar, TypeVar, cast
|
|
6
7
|
|
|
7
8
|
import click
|
|
8
9
|
from pydantic import BaseModel, ConfigDict
|
|
@@ -416,7 +417,10 @@ class WryModel(BaseModel):
|
|
|
416
417
|
if field_info.default is not PydanticUndefined:
|
|
417
418
|
config_data[field_name] = TrackedValue(field_info.default, ValueSource.DEFAULT)
|
|
418
419
|
elif field_info.default_factory is not None:
|
|
419
|
-
config_data[field_name] = TrackedValue(
|
|
420
|
+
config_data[field_name] = TrackedValue(
|
|
421
|
+
cast(Callable[[], Any], field_info.default_factory)(),
|
|
422
|
+
ValueSource.DEFAULT,
|
|
423
|
+
)
|
|
420
424
|
|
|
421
425
|
return cls.create_with_sources(config_data)
|
|
422
426
|
|
|
@@ -495,7 +499,7 @@ class WryModel(BaseModel):
|
|
|
495
499
|
config_data[field_name] = TrackedValue(field_info.default, ValueSource.DEFAULT)
|
|
496
500
|
elif field_info.default_factory is not None:
|
|
497
501
|
factory = field_info.default_factory
|
|
498
|
-
config_data[field_name] = TrackedValue(factory(), ValueSource.DEFAULT)
|
|
502
|
+
config_data[field_name] = TrackedValue(cast(Callable[[], Any], factory)(), ValueSource.DEFAULT)
|
|
499
503
|
|
|
500
504
|
# 2. Override with environment values
|
|
501
505
|
for field_name, value in env_values.items():
|
|
@@ -624,6 +628,29 @@ class WryModel(BaseModel):
|
|
|
624
628
|
if field_info.default is not PydanticUndefined:
|
|
625
629
|
result[field_name] = field_info.default
|
|
626
630
|
elif field_info.default_factory is not None:
|
|
627
|
-
result[field_name] = field_info.default_factory()
|
|
631
|
+
result[field_name] = cast(Callable[[], Any], field_info.default_factory)()
|
|
628
632
|
|
|
629
633
|
return result
|
|
634
|
+
|
|
635
|
+
@classmethod
|
|
636
|
+
def generate_click_parameters(cls) -> Callable:
|
|
637
|
+
"""Generate Click parameters decorator for this model.
|
|
638
|
+
|
|
639
|
+
This is a convenience method that allows using the decorator
|
|
640
|
+
directly from the model class without needing to import it.
|
|
641
|
+
|
|
642
|
+
Example:
|
|
643
|
+
```python
|
|
644
|
+
@click.command()
|
|
645
|
+
@MyConfig.generate_click_parameters()
|
|
646
|
+
@click.pass_context
|
|
647
|
+
def cli(ctx, **kwargs):
|
|
648
|
+
config = MyConfig.from_click_context(ctx, **kwargs)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
The generate_click_parameters decorator configured for this model
|
|
653
|
+
"""
|
|
654
|
+
from ..click_integration import generate_click_parameters
|
|
655
|
+
|
|
656
|
+
return generate_click_parameters(cls)
|
|
@@ -33,6 +33,7 @@ tests/integration/test_context_handling.py
|
|
|
33
33
|
tests/unit/__init__.py
|
|
34
34
|
tests/unit/test_auto_model_field_processing.py
|
|
35
35
|
tests/unit/test_comprehensive_imports.py
|
|
36
|
+
tests/unit/test_generate_click_classmethod.py
|
|
36
37
|
tests/unit/test_init.py
|
|
37
38
|
tests/unit/test_init_edge_cases.py
|
|
38
39
|
tests/unit/test_init_version_edge_cases.py
|
|
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
|
|
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
|
|
File without changes
|