wry 0.3.2.dev0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wry-0.3.2.dev0 → wry-0.4.0}/.gitignore +3 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/AI_KNOWLEDGE_BASE.md +131 -34
- {wry-0.3.2.dev0 → wry-0.4.0}/CHANGELOG.md +50 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/PKG-INFO +83 -9
- {wry-0.3.2.dev0 → wry-0.4.0}/README.md +82 -8
- wry-0.4.0/TODO.md +184 -0
- wry-0.4.0/examples/autowrymodel_comprehensive.py +247 -0
- wry-0.3.2.dev0/examples/multi_model_example.py → wry-0.4.0/examples/multimodel_comprehensive.py +4 -4
- wry-0.4.0/examples/wrymodel_comprehensive.py +202 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/pyproject.toml +5 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/features/test_auto_model.py +5 -5
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/features/test_multi_model.py +7 -7
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/features/test_source_precedence.py +8 -13
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/integration/test_click_edge_cases.py +6 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/integration/test_click_integration.py +7 -7
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/integration/test_click_integration_extended.py +15 -14
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/integration/test_context_handling.py +6 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_auto_model_edge_cases.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_field_annotations.py +10 -12
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_argument_types.py +6 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_bool_flag_handling.py +3 -3
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_decorator_edge_cases.py +5 -5
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_parameter_generation.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_closure_extraction_errors.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_closure_handling.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_env_vars_option.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_explicit_argument_help_injection.py +5 -5
- wry-0.4.0/tests/unit/click/test_field_alias_with_click_options.py +671 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_format_constraint_text.py +12 -12
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_json_config_loading.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_strict_mode_errors.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_type_handling.py +4 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_advanced_features.py +49 -2
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_argument_help_injection.py +6 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_exclude_enum.py +5 -5
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_generate_click_classmethod.py +3 -1
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_init_edge_cases.py +2 -2
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_multiple_option_bug.py +7 -5
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_variadic_argument_bug.py +4 -2
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/__init__.py +21 -4
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/_version.py +3 -3
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/auto_model.py +1 -1
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/click_integration.py +20 -6
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/env_utils.py +20 -3
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/model.py +49 -12
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/multi_model.py +3 -3
- {wry-0.3.2.dev0 → wry-0.4.0}/wry.egg-info/PKG-INFO +83 -9
- {wry-0.3.2.dev0 → wry-0.4.0}/wry.egg-info/SOURCES.txt +4 -12
- wry-0.3.2.dev0/TODO.md +0 -104
- wry-0.3.2.dev0/examples/argument_help_injection.py +0 -51
- wry-0.3.2.dev0/examples/auto_instantiate_edge_cases.py +0 -165
- wry-0.3.2.dev0/examples/auto_instantiate_poc.py +0 -330
- wry-0.3.2.dev0/examples/auto_model_example.py +0 -187
- wry-0.3.2.dev0/examples/explicit_argument_help.py +0 -48
- wry-0.3.2.dev0/examples/field_exclusion.py +0 -82
- wry-0.3.2.dev0/examples/field_exclusion_example.py +0 -52
- wry-0.3.2.dev0/examples/intermediate_example.py +0 -89
- wry-0.3.2.dev0/examples/simple_cli.py +0 -55
- wry-0.3.2.dev0/examples/source_tracking_comprehensive.py +0 -133
- wry-0.3.2.dev0/examples/source_tracking_example.py +0 -102
- {wry-0.3.2.dev0 → wry-0.4.0}/.github/workflows/ci-cd.yml +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/.markdownlint.json +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/.pre-commit-config.yaml +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/LICENSE +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/RELEASE_PROCESS.md +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/check.sh +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/examples/config.json +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/examples/sample_config.json +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/README.md +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/extract_release_notes.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/test_all_versions.sh +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/test_ci_locally.sh +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/test_with_act.sh +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/scripts/update-dependencies.sh +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/setup.cfg +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/README.md +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/features/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/integration/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_auto_model_annotation_inference.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_auto_model_field_processing.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_field_annotation_handling.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/auto_model/test_type_inference.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/README_TESTING_STRATEGY.md +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_config_building.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_constraint_formatting.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_interval_constraints.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_lambda_parsing.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_length_constraints.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_click_predicate_handling.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_constraint_behavior.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_constraint_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_lambda_behavior.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_lambda_error_handling.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/click/test_predicate_source_errors.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_accessors.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_core.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_env_utils.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_field_constraint_extraction.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_field_utils.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_field_utils_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_sources.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/core/test_type_checking_blocks.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_accessor_caching.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_extract_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_extract_subset_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_click_context_handling.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_data_extraction.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_default_handling.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_environment_integration.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_extract_subset_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_extraction_methods.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_field_errors.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_model_object_extraction.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_non_dict_object_extraction.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/model/test_object_attribute_extraction.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/multi_model/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/multi_model/test_multi_model.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/multi_model/test_type_checking.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_auto_model_field_processing.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_comprehensive_imports.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_help_system.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_init.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_init_version_edge_cases.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_model_extraction_methods.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_type_checking_imports.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_version_fallback.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/tests/unit/test_version_parsing.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/__init__.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/accessors.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/field_utils.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/core/sources.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/help_system.py +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry/py.typed +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry.egg-info/dependency_links.txt +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry.egg-info/requires.txt +0 -0
- {wry-0.3.2.dev0 → wry-0.4.0}/wry.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# wry - Comprehensive AI/LLM Knowledge Base
|
|
2
2
|
|
|
3
|
-
**Last Updated**: 2025-10-
|
|
4
|
-
**Version**: 0.2
|
|
3
|
+
**Last Updated**: 2025-10-04
|
|
4
|
+
**Version**: 0.3.2+
|
|
5
5
|
**Purpose**: Complete reference for AI assistants and LLMs to understand wry without reading the entire codebase
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
2. Automatically generating Click CLI parameters
|
|
15
15
|
3. Supporting multiple configuration sources (CLI/ENV/JSON/DEFAULT)
|
|
16
16
|
4. Tracking exactly where each value came from
|
|
17
|
-
5. Enforcing strict precedence: CLI >
|
|
17
|
+
5. Enforcing strict precedence: CLI > JSON > ENV > DEFAULT
|
|
18
|
+
6. **NEW v0.3.2**: Automatic alias-based CLI option generation
|
|
18
19
|
|
|
19
20
|
**Key Innovation**: Single source of truth for configuration with comprehensive source tracking.
|
|
20
21
|
|
|
21
|
-
**Stats**:
|
|
22
|
+
**Stats**: 436 tests (all passing), 92%+ coverage, supports Python 3.10-3.12, Pydantic v2.11+ compatible
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
|
@@ -75,14 +76,25 @@ tests/
|
|
|
75
76
|
└── multi_model/ # Multi-model
|
|
76
77
|
|
|
77
78
|
examples/
|
|
78
|
-
├──
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
├── autowrymodel_comprehensive.py # ⭐ Complete AutoWryModel features
|
|
80
|
+
│ - Simple options, arguments, constraints
|
|
81
|
+
│ - Pydantic aliases for custom CLI names
|
|
82
|
+
│ - Explicit click.option() for short flags
|
|
83
|
+
│ - Field exclusion with AutoExclude
|
|
84
|
+
│ - Environment variables and JSON config
|
|
85
|
+
├── wrymodel_comprehensive.py # ⭐ WryModel with source tracking
|
|
86
|
+
│ - Manual field annotation (AutoOption/AutoArgument)
|
|
87
|
+
│ - Full source tracking (CLI/ENV/JSON/DEFAULT)
|
|
88
|
+
│ - Pydantic aliases with WryModel
|
|
89
|
+
│ - Configuration precedence demonstration
|
|
90
|
+
├── multimodel_comprehensive.py # Multi-model usage
|
|
91
|
+
│ - Multiple models in one CLI
|
|
92
|
+
│ - split_kwargs_by_model
|
|
93
|
+
│ - create_models helper
|
|
94
|
+
└── config.json # Sample config
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Note**: v0.3.2 consolidated 16 example files into 3 comprehensive examples for easier discovery and learning.
|
|
86
98
|
|
|
87
99
|
---
|
|
88
100
|
|
|
@@ -96,12 +108,18 @@ examples/
|
|
|
96
108
|
|
|
97
109
|
```python
|
|
98
110
|
class WryModel(BaseModel):
|
|
99
|
-
model_config = ConfigDict(
|
|
111
|
+
model_config = ConfigDict(
|
|
112
|
+
arbitrary_types_allowed=True,
|
|
113
|
+
validate_by_name=True, # NEW v0.3.2: Accept field names
|
|
114
|
+
validate_by_alias=True # NEW v0.3.2: Accept aliases
|
|
115
|
+
)
|
|
100
116
|
env_prefix: ClassVar[str] = "" # Environment variable prefix
|
|
101
117
|
_value_sources: dict[str, ValueSource] = {} # Tracks where values came from
|
|
102
118
|
_accessor_instances: dict[str, Any] = {} # Caches property accessors
|
|
103
119
|
```
|
|
104
120
|
|
|
121
|
+
**Note**: `validate_by_name=True` and `validate_by_alias=True` enable Pydantic alias support out of the box (v0.3.2+). This allows fields to be populated using either their field name or alias, which is essential for the alias-based CLI option generation feature.
|
|
122
|
+
|
|
105
123
|
**Property Accessors** (lazy-initialized, cached):
|
|
106
124
|
|
|
107
125
|
```python
|
|
@@ -499,7 +517,7 @@ Converts constraint dict to human-readable list:
|
|
|
499
517
|
|
|
500
518
|
```python
|
|
501
519
|
@classmethod
|
|
502
|
-
def __init_subclass__(cls, **kwargs):
|
|
520
|
+
def __init_subclass__(cls, **kwargs: Any):
|
|
503
521
|
"""Called when subclass is defined."""
|
|
504
522
|
super().__init_subclass__(**kwargs)
|
|
505
523
|
|
|
@@ -538,7 +556,7 @@ def __init_subclass__(cls, **kwargs):
|
|
|
538
556
|
|
|
539
557
|
**Result**: All fields become `--options` unless explicitly configured.
|
|
540
558
|
|
|
541
|
-
**`create_auto_model(name, fields, **kwargs)`**:
|
|
559
|
+
**`create_auto_model(name, fields, **kwargs: Any)`**:
|
|
542
560
|
Dynamic model creation at runtime.
|
|
543
561
|
|
|
544
562
|
### 8. `wry/multi_model.py` - Multi-Model Support (48 lines, 100% coverage)
|
|
@@ -574,11 +592,11 @@ db = configs[DatabaseConfig]
|
|
|
574
592
|
```python
|
|
575
593
|
# Decorator that applies generate_click_parameters for each model
|
|
576
594
|
@multi_model(ServerConfig, DatabaseConfig)
|
|
577
|
-
def serve(ctx, **kwargs):
|
|
595
|
+
def serve(ctx: click.Context, **kwargs: Any):
|
|
578
596
|
# All options from both models available
|
|
579
597
|
```
|
|
580
598
|
|
|
581
|
-
**`singleton_option(*args, **kwargs)`**:
|
|
599
|
+
**`singleton_option(*args, **kwargs: Any)`**:
|
|
582
600
|
Wrapper around `click.option` that prevents duplicate options.
|
|
583
601
|
|
|
584
602
|
### 9. `wry/help_system.py` - Documentation System (89 lines)
|
|
@@ -674,7 +692,7 @@ CLICK PROCESSES (order matters!)
|
|
|
674
692
|
│ └─> Click detects: ParameterSource.ENVIRONMENT
|
|
675
693
|
│
|
|
676
694
|
└─> 4. CALL COMMAND FUNCTION
|
|
677
|
-
└─> def main(ctx, **kwargs):
|
|
695
|
+
└─> def main(ctx: click.Context, **kwargs: Any):
|
|
678
696
|
kwargs = {"host": "api.com", "port": 9000}
|
|
679
697
|
│
|
|
680
698
|
↓
|
|
@@ -877,14 +895,14 @@ class Config(AutoWryModel):
|
|
|
877
895
|
```python
|
|
878
896
|
# NO source tracking ❌
|
|
879
897
|
@Config.generate_click_parameters()
|
|
880
|
-
def main(**kwargs):
|
|
898
|
+
def main(**kwargs: Any):
|
|
881
899
|
config = Config(**kwargs)
|
|
882
900
|
# config.source.* always shows CLI
|
|
883
901
|
|
|
884
902
|
# FULL source tracking ✅
|
|
885
903
|
@Config.generate_click_parameters()
|
|
886
904
|
@click.pass_context
|
|
887
|
-
def main(ctx, **kwargs):
|
|
905
|
+
def main(ctx: click.Context, **kwargs: Any):
|
|
888
906
|
config = Config.from_click_context(ctx, **kwargs)
|
|
889
907
|
# config.source.* shows actual source
|
|
890
908
|
```
|
|
@@ -1007,7 +1025,7 @@ class Config(AutoWryModel):
|
|
|
1007
1025
|
|
|
1008
1026
|
@click.command()
|
|
1009
1027
|
@Config.generate_click_parameters()
|
|
1010
|
-
def main(**kwargs):
|
|
1028
|
+
def main(**kwargs: Any):
|
|
1011
1029
|
config = Config(**kwargs)
|
|
1012
1030
|
print(f"{config.host}:{config.port}")
|
|
1013
1031
|
```
|
|
@@ -1034,7 +1052,7 @@ class Config(AutoWryModel):
|
|
|
1034
1052
|
@click.command()
|
|
1035
1053
|
@Config.generate_click_parameters()
|
|
1036
1054
|
@click.pass_context # ⚠️ Required
|
|
1037
|
-
def main(ctx, **kwargs): # ⚠️ ctx parameter required
|
|
1055
|
+
def main(ctx: click.Context, **kwargs: Any): # ⚠️ ctx parameter required
|
|
1038
1056
|
config = Config.from_click_context(ctx, **kwargs) # ⚠️ Not Config(**kwargs)
|
|
1039
1057
|
|
|
1040
1058
|
# Access sources
|
|
@@ -1062,7 +1080,7 @@ class Config(WryModel):
|
|
|
1062
1080
|
|
|
1063
1081
|
@click.command()
|
|
1064
1082
|
@Config.generate_click_parameters()
|
|
1065
|
-
def main(**kwargs):
|
|
1083
|
+
def main(**kwargs: Any):
|
|
1066
1084
|
config = Config(**kwargs)
|
|
1067
1085
|
# Usage: python app.py input.txt output.txt --format yaml --verbose
|
|
1068
1086
|
```
|
|
@@ -1106,7 +1124,7 @@ class DatabaseConfig(WryModel):
|
|
|
1106
1124
|
@click.command()
|
|
1107
1125
|
@multi_model(ServerConfig, DatabaseConfig)
|
|
1108
1126
|
@click.pass_context
|
|
1109
|
-
def serve(ctx, **kwargs):
|
|
1127
|
+
def serve(ctx: click.Context, **kwargs: Any):
|
|
1110
1128
|
configs = create_models(ctx, kwargs, ServerConfig, DatabaseConfig)
|
|
1111
1129
|
server = configs[ServerConfig]
|
|
1112
1130
|
db = configs[DatabaseConfig]
|
|
@@ -1137,6 +1155,85 @@ class Config(AutoWryModel):
|
|
|
1137
1155
|
- Polymorphic validation fields
|
|
1138
1156
|
- Secrets not from CLI
|
|
1139
1157
|
|
|
1158
|
+
### Pattern 7: Pydantic Aliases for Custom CLI Names (v0.3.2+)
|
|
1159
|
+
|
|
1160
|
+
**New in v0.3.2**: Pydantic field aliases automatically control CLI option names and environment variable names!
|
|
1161
|
+
|
|
1162
|
+
```python
|
|
1163
|
+
from pydantic import Field
|
|
1164
|
+
from wry import AutoWryModel
|
|
1165
|
+
|
|
1166
|
+
class DatabaseConfig(AutoWryModel):
|
|
1167
|
+
env_prefix = "DB_"
|
|
1168
|
+
|
|
1169
|
+
# Concise Python field name: db_url
|
|
1170
|
+
# Alias controls CLI option: --database-url
|
|
1171
|
+
# Environment variable: DB_DATABASE_URL
|
|
1172
|
+
db_url: str = Field(
|
|
1173
|
+
alias="database_url",
|
|
1174
|
+
default="sqlite:///app.db",
|
|
1175
|
+
description="Database connection URL"
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
pool_size: int = Field(
|
|
1179
|
+
alias="connection_pool_size",
|
|
1180
|
+
default=5,
|
|
1181
|
+
description="Maximum connection pool size"
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
@click.command()
|
|
1185
|
+
@DatabaseConfig.generate_click_parameters()
|
|
1186
|
+
@click.pass_context
|
|
1187
|
+
def main(ctx: click.Context, **kwargs: Any):
|
|
1188
|
+
config = DatabaseConfig.from_click_context(ctx, **kwargs)
|
|
1189
|
+
|
|
1190
|
+
# Access via concise field names
|
|
1191
|
+
print(f"URL: {config.db_url}")
|
|
1192
|
+
print(f"Pool: {config.pool_size}")
|
|
1193
|
+
|
|
1194
|
+
# Source tracking works
|
|
1195
|
+
print(f"URL from: {config.source.db_url.value}")
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
**How it works**:
|
|
1199
|
+
|
|
1200
|
+
- **Python field**: `db_url` (concise, easy to type in code)
|
|
1201
|
+
- **CLI option**: `--database-url` (descriptive, user-friendly)
|
|
1202
|
+
- **Environment variable**: `DB_DATABASE_URL` (consistent with CLI)
|
|
1203
|
+
- **JSON config**: Accepts both `db_url` and `database_url`
|
|
1204
|
+
|
|
1205
|
+
**Requirements**:
|
|
1206
|
+
|
|
1207
|
+
- **None!** WryModel automatically sets `validate_by_name=True` and `validate_by_alias=True`
|
|
1208
|
+
- No configuration needed - it just works!
|
|
1209
|
+
|
|
1210
|
+
**Full support**:
|
|
1211
|
+
|
|
1212
|
+
- ✅ Aliases automatically control auto-generated option names
|
|
1213
|
+
- ✅ Environment variables use alias names (consistent with CLI)
|
|
1214
|
+
- ✅ Source tracking works correctly
|
|
1215
|
+
- ✅ JSON config accepts both field names and aliases
|
|
1216
|
+
|
|
1217
|
+
**Why this pattern exists:**
|
|
1218
|
+
|
|
1219
|
+
Before v0.3.2, custom CLI option names required explicit `click.option()` decorators for every field. The alias feature eliminates this boilerplate for the common case.
|
|
1220
|
+
|
|
1221
|
+
**For advanced use cases** (short options, custom Click types), combine aliases with explicit decorators:
|
|
1222
|
+
|
|
1223
|
+
```python
|
|
1224
|
+
class Config(AutoWryModel):
|
|
1225
|
+
# Explicit click.option for short option support
|
|
1226
|
+
verbose: Annotated[int, click.option("-v", "--verbose", count=True)] = Field(default=0)
|
|
1227
|
+
|
|
1228
|
+
# Or combine with aliases for descriptive field names
|
|
1229
|
+
database_connection_string: Annotated[
|
|
1230
|
+
str,
|
|
1231
|
+
click.option("--db-url", "-d", default="sqlite:///app.db")
|
|
1232
|
+
] = Field(alias='db_url', default="sqlite:///app.db")
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
See `examples/autowrymodel_comprehensive.py` and `examples/wrymodel_comprehensive.py` for complete examples.
|
|
1236
|
+
|
|
1140
1237
|
---
|
|
1141
1238
|
|
|
1142
1239
|
## Test Coverage Details
|
|
@@ -1280,10 +1377,10 @@ format_constraint_text(constraints) → list[str]
|
|
|
1280
1377
|
multi_model(*model_classes, strict=False) → decorator
|
|
1281
1378
|
create_models(ctx, kwargs, *model_classes) → dict[type, instance]
|
|
1282
1379
|
split_kwargs_by_model(kwargs, *model_classes) → dict[type, dict]
|
|
1283
|
-
singleton_option(*args, **kwargs) → decorator
|
|
1380
|
+
singleton_option(*args, **kwargs: Any) → decorator
|
|
1284
1381
|
|
|
1285
1382
|
# Auto Model
|
|
1286
|
-
create_auto_model(name, fields, **kwargs) → type[AutoWryModel]
|
|
1383
|
+
create_auto_model(name, fields, **kwargs: Any) → type[AutoWryModel]
|
|
1287
1384
|
|
|
1288
1385
|
# Field Utilities
|
|
1289
1386
|
extract_field_constraints(field_info) → dict[str, Any]
|
|
@@ -1442,7 +1539,7 @@ python -c "import wry; print(wry.__version__)"
|
|
|
1442
1539
|
# WRONG ❌
|
|
1443
1540
|
@click.command()
|
|
1444
1541
|
@Config.generate_click_parameters()
|
|
1445
|
-
def main(**kwargs): # No ctx parameter
|
|
1542
|
+
def main(**kwargs: Any): # No ctx parameter
|
|
1446
1543
|
config = Config(**kwargs) # Direct instantiation
|
|
1447
1544
|
# Result: All sources show as CLI
|
|
1448
1545
|
|
|
@@ -1450,7 +1547,7 @@ def main(**kwargs): # No ctx parameter
|
|
|
1450
1547
|
@click.command()
|
|
1451
1548
|
@Config.generate_click_parameters()
|
|
1452
1549
|
@click.pass_context # Add this
|
|
1453
|
-
def main(ctx, **kwargs): # Add ctx parameter
|
|
1550
|
+
def main(ctx: click.Context, **kwargs: Any): # Add ctx parameter
|
|
1454
1551
|
config = Config.from_click_context(ctx, **kwargs) # Use this
|
|
1455
1552
|
# Result: Accurate source tracking
|
|
1456
1553
|
```
|
|
@@ -1647,7 +1744,7 @@ config_data = {"host": TrackedValue("api.com", ValueSource.CLI)}
|
|
|
1647
1744
|
### Why model_dump() Excludes Accessors?
|
|
1648
1745
|
|
|
1649
1746
|
```python
|
|
1650
|
-
def model_dump(self, **kwargs) -> dict[str, Any]:
|
|
1747
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
1651
1748
|
data = super().model_dump(**kwargs)
|
|
1652
1749
|
accessor_keys = {"source", "minimum", "maximum", "constraints", "defaults"}
|
|
1653
1750
|
return {k: v for k, v in data.items() if k not in accessor_keys}
|
|
@@ -1806,7 +1903,7 @@ class Config(AutoWryModel):
|
|
|
1806
1903
|
|
|
1807
1904
|
```python
|
|
1808
1905
|
@click.pass_context
|
|
1809
|
-
def main(ctx, **kwargs):
|
|
1906
|
+
def main(ctx: click.Context, **kwargs: Any):
|
|
1810
1907
|
config = Config(**kwargs) # ⚠️ Not using context
|
|
1811
1908
|
```
|
|
1812
1909
|
|
|
@@ -1837,7 +1934,7 @@ class Config(AutoWryModel):
|
|
|
1837
1934
|
|
|
1838
1935
|
@click.command()
|
|
1839
1936
|
@Config.generate_click_parameters()
|
|
1840
|
-
def main(**kwargs):
|
|
1937
|
+
def main(**kwargs: Any):
|
|
1841
1938
|
config = Config(**kwargs)
|
|
1842
1939
|
print(f"Connecting to {config.host}:{config.port}")
|
|
1843
1940
|
|
|
@@ -1881,7 +1978,7 @@ class Config(AutoWryModel):
|
|
|
1881
1978
|
@click.command()
|
|
1882
1979
|
@Config.generate_click_parameters()
|
|
1883
1980
|
@click.pass_context # ⚠️ Required for source tracking
|
|
1884
|
-
def main(ctx, **kwargs): # ⚠️ ctx parameter required
|
|
1981
|
+
def main(ctx: click.Context, **kwargs: Any): # ⚠️ ctx parameter required
|
|
1885
1982
|
"""Process files with configuration."""
|
|
1886
1983
|
config = Config.from_click_context(ctx, **kwargs)
|
|
1887
1984
|
|
|
@@ -1943,7 +2040,7 @@ class DatabaseConfig(WryModel):
|
|
|
1943
2040
|
@click.command()
|
|
1944
2041
|
@multi_model(ServerConfig, DatabaseConfig)
|
|
1945
2042
|
@click.pass_context
|
|
1946
|
-
def serve(ctx, **kwargs):
|
|
2043
|
+
def serve(ctx: click.Context, **kwargs: Any):
|
|
1947
2044
|
"""Start server with database connection."""
|
|
1948
2045
|
# Create both models from kwargs
|
|
1949
2046
|
configs = create_models(ctx, kwargs, ServerConfig, DatabaseConfig)
|
|
@@ -2040,7 +2137,7 @@ Config = create_auto_model(
|
|
|
2040
2137
|
# Use like any AutoWryModel
|
|
2041
2138
|
@click.command()
|
|
2042
2139
|
@Config.generate_click_parameters()
|
|
2043
|
-
def main(**kwargs):
|
|
2140
|
+
def main(**kwargs: Any):
|
|
2044
2141
|
config = Config(**kwargs)
|
|
2045
2142
|
print(f"{config.host}:{config.port}")
|
|
2046
2143
|
```
|
|
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2025-10-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Automatic Pydantic alias-based CLI option generation** 🎉
|
|
15
|
+
- Aliases now automatically control generated CLI option names and environment variable names
|
|
16
|
+
- Example: `db_url` field with `alias="database_url"` generates `--database-url` option and `DB_DATABASE_URL` env var
|
|
17
|
+
- No configuration required - works out of the box!
|
|
18
|
+
- `WryModel` now sets `validate_by_name=True` and `validate_by_alias=True` by default
|
|
19
|
+
- Full source tracking support (CLI/ENV/JSON/DEFAULT) with aliases
|
|
20
|
+
- JSON config files accept both field names and aliases
|
|
21
|
+
- New example: `examples/field_alias_example.py` demonstrating automatic alias-based generation
|
|
22
|
+
- Note: For short options (e.g., `-v`), continue using explicit `click.option()` decorators as shown in `examples/auto_model_example.py`
|
|
23
|
+
- Comprehensive test suite: 20 tests covering all alias scenarios including precedence chains
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **Pydantic v2.11+ compatibility** - replaced deprecated `populate_by_name` with `validate_by_name` and `validate_by_alias`
|
|
28
|
+
- `WryModel.model_config` now includes `validate_by_name=True` and `validate_by_alias=True` by default
|
|
29
|
+
- `generate_click_parameters()` now uses field aliases for auto-generated option names
|
|
30
|
+
- `get_env_var_names()` now uses field aliases for environment variable names
|
|
31
|
+
- Updated all documentation (README, AI_KNOWLEDGE_BASE, TODO) with alias feature details
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Source tracking now works correctly when using Pydantic aliases
|
|
36
|
+
- JSON config loading properly handles both field names and aliases
|
|
37
|
+
- Parameter source detection checks both alias and field name for Click compatibility
|
|
38
|
+
- Removed all deprecated `Field(annotation=...)` usage in tests
|
|
39
|
+
- All deprecation warnings eliminated for Pydantic v3 compatibility
|
|
40
|
+
- Click now properly validates required arguments and options, providing native Click error messages
|
|
41
|
+
- Fixed type display in `--show-env-vars` to correctly extract types from Annotated fields
|
|
42
|
+
|
|
43
|
+
### Technical Details
|
|
44
|
+
|
|
45
|
+
- Modified `from_click_context()` to build alias-to-field mapping and handle both field names and aliases
|
|
46
|
+
- Enhanced kwargs filtering to accept both field names and aliases, mapping aliases back to field names for source tracking
|
|
47
|
+
- Updated JSON config handling to map aliases to field names
|
|
48
|
+
- Improved parameter source checking with alias fallback
|
|
49
|
+
- Updated `generate_click_parameters()` to use aliases for option name generation
|
|
50
|
+
- Updated `get_env_var_names()` to use aliases for environment variable names
|
|
51
|
+
- Modified argument generation to properly handle `required` flag based on field defaults and environment variables
|
|
52
|
+
- Removed forced `required=False` override in `extract_and_modify_argument_decorator()` to preserve original Click behavior
|
|
53
|
+
- Improved `print_env_vars()` to correctly extract base types from Annotated fields
|
|
54
|
+
- Consolidated 16 example files into 3 comprehensive examples for easier discovery:
|
|
55
|
+
- `examples/autowrymodel_comprehensive.py` - All AutoWryModel features
|
|
56
|
+
- `examples/wrymodel_comprehensive.py` - WryModel with source tracking
|
|
57
|
+
- `examples/multimodel_comprehensive.py` - Multi-model usage
|
|
58
|
+
- All 436 tests pass with 92%+ code coverage
|
|
59
|
+
|
|
10
60
|
## [0.3.1] - 2025-10-02
|
|
11
61
|
|
|
12
62
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wry
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Why Repeat Yourself? - Define your CLI once with Pydantic models
|
|
5
5
|
Author-email: Tyler House <26489166+tahouse@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
@@ -90,7 +90,7 @@ class AppArgs(AutoWryModel):
|
|
|
90
90
|
|
|
91
91
|
@click.command()
|
|
92
92
|
@AppArgs.generate_click_parameters()
|
|
93
|
-
def main(**kwargs):
|
|
93
|
+
def main(**kwargs: Any):
|
|
94
94
|
"""My simple CLI application."""
|
|
95
95
|
# Create the model instance from kwargs
|
|
96
96
|
config = AppArgs(**kwargs)
|
|
@@ -100,7 +100,10 @@ if __name__ == "__main__":
|
|
|
100
100
|
main()
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
**
|
|
103
|
+
**See comprehensive examples:**
|
|
104
|
+
- `examples/autowrymodel_comprehensive.py` - All AutoWryModel features including aliases
|
|
105
|
+
- `examples/wrymodel_comprehensive.py` - WryModel with source tracking
|
|
106
|
+
- `examples/multimodel_comprehensive.py` - Multi-model usage
|
|
104
107
|
|
|
105
108
|
Run it:
|
|
106
109
|
|
|
@@ -129,7 +132,7 @@ wry tracks where each configuration value came from, supporting all four sources
|
|
|
129
132
|
```python
|
|
130
133
|
@click.command()
|
|
131
134
|
@AppArgs.generate_click_parameters()
|
|
132
|
-
def main(**kwargs):
|
|
135
|
+
def main(**kwargs: Any):
|
|
133
136
|
# Simple instantiation - no source tracking
|
|
134
137
|
config = AppArgs(**kwargs)
|
|
135
138
|
# Works fine, but config.source.* will always show CLI
|
|
@@ -143,7 +146,7 @@ To enable accurate source tracking, use `@click.pass_context` and `from_click_co
|
|
|
143
146
|
@click.command()
|
|
144
147
|
@AppArgs.generate_click_parameters()
|
|
145
148
|
@click.pass_context
|
|
146
|
-
def main(ctx, **kwargs):
|
|
149
|
+
def main(ctx: click.Context, **kwargs: Any):
|
|
147
150
|
# Full source tracking with context
|
|
148
151
|
config = AppArgs.from_click_context(ctx, **kwargs)
|
|
149
152
|
|
|
@@ -181,7 +184,8 @@ python examples/source_tracking_comprehensive.py --config examples/sample_config
|
|
|
181
184
|
```
|
|
182
185
|
|
|
183
186
|
**Output shows source for each field:**
|
|
184
|
-
|
|
187
|
+
|
|
188
|
+
```text
|
|
185
189
|
host = json-server.com [from JSON]
|
|
186
190
|
port = 3000 [from CLI] ← CLI overrides JSON
|
|
187
191
|
debug = True [from ENV]
|
|
@@ -257,7 +261,7 @@ class DatabaseArgs(WryModel):
|
|
|
257
261
|
@click.command()
|
|
258
262
|
@multi_model(ServerConfig, DatabaseConfig)
|
|
259
263
|
@click.pass_context
|
|
260
|
-
def serve(ctx, **kwargs):
|
|
264
|
+
def serve(ctx: click.Context, **kwargs: Any):
|
|
261
265
|
# Create model instances
|
|
262
266
|
configs = create_models(ctx, kwargs, ServerConfig, DatabaseConfig)
|
|
263
267
|
server = configs[ServerConfig]
|
|
@@ -330,7 +334,7 @@ class AppArgs(WryModel):
|
|
|
330
334
|
@click.command()
|
|
331
335
|
@multi_model(DatabaseConfig, AppArgs)
|
|
332
336
|
@click.pass_context
|
|
333
|
-
def main(ctx, **kwargs):
|
|
337
|
+
def main(ctx: click.Context, **kwargs: Any):
|
|
334
338
|
# Automatically splits kwargs between models
|
|
335
339
|
configs = create_models(ctx, kwargs, DatabaseConfig, AppArgs)
|
|
336
340
|
|
|
@@ -349,7 +353,7 @@ By default, `generate_click_parameters` runs in strict mode to prevent common mi
|
|
|
349
353
|
@click.command()
|
|
350
354
|
@Config.generate_click_parameters() # strict=True by default
|
|
351
355
|
@Config.generate_click_parameters() # ERROR: Duplicate decorator detected!
|
|
352
|
-
def main(**kwargs):
|
|
356
|
+
def main(**kwargs: Any):
|
|
353
357
|
pass
|
|
354
358
|
```
|
|
355
359
|
|
|
@@ -391,6 +395,76 @@ class Config(WryModel):
|
|
|
391
395
|
)
|
|
392
396
|
```
|
|
393
397
|
|
|
398
|
+
### Using Pydantic Aliases for Custom CLI Names
|
|
399
|
+
|
|
400
|
+
**New in v0.3.2+**: Pydantic field aliases automatically control the generated CLI option names and environment variable names!
|
|
401
|
+
|
|
402
|
+
This allows you to have concise Python field names while exposing descriptive CLI options:
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
from pydantic import Field
|
|
406
|
+
from wry import AutoWryModel
|
|
407
|
+
|
|
408
|
+
class DatabaseConfig(AutoWryModel):
|
|
409
|
+
env_prefix = "DB_"
|
|
410
|
+
|
|
411
|
+
# Concise Python field name: db_url
|
|
412
|
+
# Alias controls CLI option: --database-url
|
|
413
|
+
# Environment variable: DB_DATABASE_URL
|
|
414
|
+
db_url: str = Field(
|
|
415
|
+
alias="database_url",
|
|
416
|
+
default="sqlite:///app.db",
|
|
417
|
+
description="Database connection URL"
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
pool_size: int = Field(
|
|
421
|
+
alias="connection_pool_size",
|
|
422
|
+
default=5,
|
|
423
|
+
description="Maximum connection pool size"
|
|
424
|
+
)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**How it works:**
|
|
428
|
+
|
|
429
|
+
- **Python field**: `db_url` (concise, easy to type)
|
|
430
|
+
- **CLI option**: `--database-url` (descriptive, user-friendly)
|
|
431
|
+
- **Environment variable**: `DB_DATABASE_URL` (consistent with CLI)
|
|
432
|
+
- **JSON config**: Accepts both `db_url` and `database_url`
|
|
433
|
+
|
|
434
|
+
**Requirements:**
|
|
435
|
+
|
|
436
|
+
- **None!** `WryModel` automatically sets `validate_by_name=True` and `validate_by_alias=True`
|
|
437
|
+
- This tells Pydantic to accept both field names and aliases
|
|
438
|
+
- No need to configure anything - it just works!
|
|
439
|
+
- Aliases automatically control option names, env var names, and help text
|
|
440
|
+
|
|
441
|
+
**Full support (v0.3.2+):**
|
|
442
|
+
|
|
443
|
+
- ✅ Aliases automatically control auto-generated option names
|
|
444
|
+
- ✅ Environment variables use alias names (consistent with CLI)
|
|
445
|
+
- ✅ Source tracking works correctly
|
|
446
|
+
- ✅ JSON config accepts both field names and aliases
|
|
447
|
+
|
|
448
|
+
**Why this feature exists:**
|
|
449
|
+
|
|
450
|
+
Before v0.3.2, if you wanted custom CLI option names, you had to use explicit `click.option()` decorators for every field. The alias feature eliminates this boilerplate for the common case where you just want different names.
|
|
451
|
+
|
|
452
|
+
**For advanced use cases** (short options, custom Click types):
|
|
453
|
+
|
|
454
|
+
You can still combine aliases with explicit `click.option()` decorators:
|
|
455
|
+
|
|
456
|
+
```python
|
|
457
|
+
class Config(AutoWryModel):
|
|
458
|
+
# Explicit click.option for short option support
|
|
459
|
+
verbose: Annotated[int, click.option("-v", "--verbose", count=True)] = Field(default=0)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
See `examples/autowrymodel_comprehensive.py` for examples of explicit Click decorators.
|
|
463
|
+
|
|
464
|
+
**See also:**
|
|
465
|
+
- `examples/autowrymodel_comprehensive.py` - Complete AutoWryModel example with aliases
|
|
466
|
+
- `examples/wrymodel_comprehensive.py` - WryModel with aliases and source tracking
|
|
467
|
+
|
|
394
468
|
## Development
|
|
395
469
|
|
|
396
470
|
### Prerequisites
|