wry 0.4.1__tar.gz → 0.5.1.dev3__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.
Files changed (138) hide show
  1. wry-0.5.1.dev3/.cursorrules +192 -0
  2. {wry-0.4.1 → wry-0.5.1.dev3}/AI_KNOWLEDGE_BASE.md +159 -1
  3. {wry-0.4.1 → wry-0.5.1.dev3}/CHANGELOG.md +100 -0
  4. wry-0.5.1.dev3/CONTRIBUTING.md +633 -0
  5. {wry-0.4.1/wry.egg-info → wry-0.5.1.dev3}/PKG-INFO +153 -1
  6. {wry-0.4.1 → wry-0.5.1.dev3}/README.md +152 -0
  7. wry-0.5.1.dev3/tests/unit/auto_model/test_auto_model_list_fields.py +433 -0
  8. wry-0.5.1.dev3/tests/unit/auto_model/test_comma_separated_lists.py +508 -0
  9. {wry-0.4.1 → wry-0.5.1.dev3}/wry/__init__.py +4 -0
  10. {wry-0.4.1 → wry-0.5.1.dev3}/wry/_version.py +3 -3
  11. {wry-0.4.1 → wry-0.5.1.dev3}/wry/auto_model.py +8 -1
  12. {wry-0.4.1 → wry-0.5.1.dev3}/wry/click_integration.py +40 -2
  13. wry-0.5.1.dev3/wry/comma_separated.py +182 -0
  14. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/model.py +1 -0
  15. {wry-0.4.1 → wry-0.5.1.dev3/wry.egg-info}/PKG-INFO +153 -1
  16. {wry-0.4.1 → wry-0.5.1.dev3}/wry.egg-info/SOURCES.txt +5 -0
  17. {wry-0.4.1 → wry-0.5.1.dev3}/.github/workflows/ci-cd.yml +0 -0
  18. {wry-0.4.1 → wry-0.5.1.dev3}/.gitignore +0 -0
  19. {wry-0.4.1 → wry-0.5.1.dev3}/.markdownlint.json +0 -0
  20. {wry-0.4.1 → wry-0.5.1.dev3}/.pre-commit-config.yaml +0 -0
  21. {wry-0.4.1 → wry-0.5.1.dev3}/LICENSE +0 -0
  22. {wry-0.4.1 → wry-0.5.1.dev3}/RELEASE_PROCESS.md +0 -0
  23. {wry-0.4.1 → wry-0.5.1.dev3}/TODO.md +0 -0
  24. {wry-0.4.1 → wry-0.5.1.dev3}/check.sh +0 -0
  25. {wry-0.4.1 → wry-0.5.1.dev3}/examples/autowrymodel_comprehensive.py +0 -0
  26. {wry-0.4.1 → wry-0.5.1.dev3}/examples/config.json +0 -0
  27. {wry-0.4.1 → wry-0.5.1.dev3}/examples/multimodel_comprehensive.py +0 -0
  28. {wry-0.4.1 → wry-0.5.1.dev3}/examples/sample_config.json +0 -0
  29. {wry-0.4.1 → wry-0.5.1.dev3}/examples/wrymodel_comprehensive.py +0 -0
  30. {wry-0.4.1 → wry-0.5.1.dev3}/pyproject.toml +0 -0
  31. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/README.md +0 -0
  32. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/extract_release_notes.py +0 -0
  33. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/test_all_versions.sh +0 -0
  34. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/test_ci_locally.sh +0 -0
  35. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/test_with_act.sh +0 -0
  36. {wry-0.4.1 → wry-0.5.1.dev3}/scripts/update-dependencies.sh +0 -0
  37. {wry-0.4.1 → wry-0.5.1.dev3}/setup.cfg +0 -0
  38. {wry-0.4.1 → wry-0.5.1.dev3}/tests/README.md +0 -0
  39. {wry-0.4.1 → wry-0.5.1.dev3}/tests/__init__.py +0 -0
  40. {wry-0.4.1 → wry-0.5.1.dev3}/tests/features/__init__.py +0 -0
  41. {wry-0.4.1 → wry-0.5.1.dev3}/tests/features/test_auto_model.py +0 -0
  42. {wry-0.4.1 → wry-0.5.1.dev3}/tests/features/test_inheritance.py +0 -0
  43. {wry-0.4.1 → wry-0.5.1.dev3}/tests/features/test_multi_model.py +0 -0
  44. {wry-0.4.1 → wry-0.5.1.dev3}/tests/features/test_source_precedence.py +0 -0
  45. {wry-0.4.1 → wry-0.5.1.dev3}/tests/integration/__init__.py +0 -0
  46. {wry-0.4.1 → wry-0.5.1.dev3}/tests/integration/test_click_edge_cases.py +0 -0
  47. {wry-0.4.1 → wry-0.5.1.dev3}/tests/integration/test_click_integration.py +0 -0
  48. {wry-0.4.1 → wry-0.5.1.dev3}/tests/integration/test_click_integration_extended.py +0 -0
  49. {wry-0.4.1 → wry-0.5.1.dev3}/tests/integration/test_context_handling.py +0 -0
  50. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/__init__.py +0 -0
  51. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/__init__.py +0 -0
  52. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_auto_model_annotation_inference.py +0 -0
  53. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_auto_model_edge_cases.py +0 -0
  54. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_auto_model_field_processing.py +0 -0
  55. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_field_annotation_handling.py +0 -0
  56. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_field_annotations.py +0 -0
  57. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/auto_model/test_type_inference.py +0 -0
  58. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/README_TESTING_STRATEGY.md +0 -0
  59. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/__init__.py +0 -0
  60. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_argument_types.py +0 -0
  61. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_bool_flag_handling.py +0 -0
  62. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_config_building.py +0 -0
  63. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_constraint_formatting.py +0 -0
  64. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_decorator_edge_cases.py +0 -0
  65. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_interval_constraints.py +0 -0
  66. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_lambda_parsing.py +0 -0
  67. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_length_constraints.py +0 -0
  68. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_parameter_generation.py +0 -0
  69. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_click_predicate_handling.py +0 -0
  70. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_closure_extraction_errors.py +0 -0
  71. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_closure_handling.py +0 -0
  72. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_constraint_behavior.py +0 -0
  73. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_constraint_edge_cases.py +0 -0
  74. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_env_vars_option.py +0 -0
  75. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_explicit_argument_help_injection.py +0 -0
  76. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_field_alias_with_click_options.py +0 -0
  77. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_format_constraint_text.py +0 -0
  78. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_json_config_loading.py +0 -0
  79. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_lambda_behavior.py +0 -0
  80. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_lambda_error_handling.py +0 -0
  81. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_predicate_source_errors.py +0 -0
  82. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_strict_mode_errors.py +0 -0
  83. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/click/test_type_handling.py +0 -0
  84. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/__init__.py +0 -0
  85. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_accessors.py +0 -0
  86. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_advanced_features.py +0 -0
  87. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_core.py +0 -0
  88. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_edge_cases.py +0 -0
  89. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_env_utils.py +0 -0
  90. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_field_constraint_extraction.py +0 -0
  91. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_field_utils.py +0 -0
  92. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_field_utils_edge_cases.py +0 -0
  93. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_sources.py +0 -0
  94. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/core/test_type_checking_blocks.py +0 -0
  95. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/__init__.py +0 -0
  96. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_accessor_caching.py +0 -0
  97. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_extract_edge_cases.py +0 -0
  98. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_extract_subset_edge_cases.py +0 -0
  99. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_click_context_handling.py +0 -0
  100. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_data_extraction.py +0 -0
  101. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_default_handling.py +0 -0
  102. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_edge_cases.py +0 -0
  103. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_environment_integration.py +0 -0
  104. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_extract_subset_edge_cases.py +0 -0
  105. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_extraction_methods.py +0 -0
  106. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_field_errors.py +0 -0
  107. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_model_object_extraction.py +0 -0
  108. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_non_dict_object_extraction.py +0 -0
  109. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/model/test_object_attribute_extraction.py +0 -0
  110. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/multi_model/__init__.py +0 -0
  111. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/multi_model/test_multi_model.py +0 -0
  112. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/multi_model/test_type_checking.py +0 -0
  113. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_argument_help_injection.py +0 -0
  114. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_auto_model_field_processing.py +0 -0
  115. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_comprehensive_imports.py +0 -0
  116. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_exclude_enum.py +0 -0
  117. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_generate_click_classmethod.py +0 -0
  118. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_help_system.py +0 -0
  119. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_init.py +0 -0
  120. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_init_edge_cases.py +0 -0
  121. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_init_version_edge_cases.py +0 -0
  122. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_model_extraction_methods.py +0 -0
  123. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_multiple_option_bug.py +0 -0
  124. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_type_checking_imports.py +0 -0
  125. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_variadic_argument_bug.py +0 -0
  126. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_version_fallback.py +0 -0
  127. {wry-0.4.1 → wry-0.5.1.dev3}/tests/unit/test_version_parsing.py +0 -0
  128. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/__init__.py +0 -0
  129. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/accessors.py +0 -0
  130. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/env_utils.py +0 -0
  131. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/field_utils.py +0 -0
  132. {wry-0.4.1 → wry-0.5.1.dev3}/wry/core/sources.py +0 -0
  133. {wry-0.4.1 → wry-0.5.1.dev3}/wry/help_system.py +0 -0
  134. {wry-0.4.1 → wry-0.5.1.dev3}/wry/multi_model.py +0 -0
  135. {wry-0.4.1 → wry-0.5.1.dev3}/wry/py.typed +0 -0
  136. {wry-0.4.1 → wry-0.5.1.dev3}/wry.egg-info/dependency_links.txt +0 -0
  137. {wry-0.4.1 → wry-0.5.1.dev3}/wry.egg-info/requires.txt +0 -0
  138. {wry-0.4.1 → wry-0.5.1.dev3}/wry.egg-info/top_level.txt +0 -0
@@ -0,0 +1,192 @@
1
+ # Cursor AI Rules for wry Repository
2
+
3
+ > **Note**: These rules are the AI assistant's quick reference. For comprehensive guidelines,
4
+ > examples, and detailed explanations, see `CONTRIBUTING.md`.
5
+
6
+ ## Pre-Commit Requirements
7
+
8
+ Before making any commit, AI assistants MUST:
9
+
10
+ 1. **Update Documentation**:
11
+ - README.md if user-facing changes
12
+ - AI_KNOWLEDGE_BASE.md if architecture/API changes
13
+ - CHANGELOG.md with entry under [Unreleased]
14
+ - Module docstrings if relevant
15
+
16
+ 2. **Ensure Code Quality**:
17
+ - Add type annotations to all new functions
18
+ - Add docstrings to all public functions/classes
19
+ - Follow DRY principles
20
+ - Remove dead code and unused imports
21
+
22
+ 3. **Update/Add Tests**:
23
+ - Add tests for new functionality
24
+ - Update tests for changed behavior
25
+ - Ensure tests pass (run pytest)
26
+
27
+ 4. **Run Checks**:
28
+ - Ensure pre-commit hooks will pass
29
+ - Fix linter errors (ruff, mypy)
30
+ - Format code properly (ruff format)
31
+
32
+ 5. **Follow Conventional Commits**:
33
+ - Use format: `type: description`
34
+ - Types: feat, fix, docs, refactor, test, chore, build
35
+
36
+ ## Reference
37
+
38
+ **Full development guidelines**: See `CONTRIBUTING.md` for:
39
+
40
+ - Complete code organization principles
41
+ - Detailed examples of adding features
42
+ - wry-specific development patterns
43
+ - Comprehensive pre-commit checklist
44
+ - Pull request process
45
+
46
+ **Quick links**:
47
+
48
+ - Architecture & concepts: `CONTRIBUTING.md` "Core Concepts" section
49
+ - Code patterns: `CONTRIBUTING.md` "Configuration Models" section
50
+ - Testing guide: `CONTRIBUTING.md` "Testing" section
51
+ - Pre-commit checklist: `CONTRIBUTING.md` "Pre-Commit Checklist" section
52
+
53
+ ## Enforcement
54
+
55
+ The pre-commit hook automatically runs checks on commit.
56
+ All checks must pass before commit succeeds.
57
+
58
+ ## General Development Guidelines
59
+
60
+ ### Date and Time
61
+
62
+ - Use the `date` terminal command to learn the current date
63
+ - Never assume or hardcode dates in dynamic contexts
64
+
65
+ ### Python Best Practices
66
+
67
+ #### Type Annotations
68
+
69
+ - **ALWAYS** add type annotations to all functions, methods, and class attributes
70
+ - Include return types for all functions (use `-> None` for void functions)
71
+ - Use proper generic types from `typing` module
72
+ - Prefer modern Python 3.10+ syntax (e.g., `list[str]` over `List[str]`)
73
+
74
+ #### Documentation
75
+
76
+ - Add comprehensive docstrings to:
77
+ - All modules (at the top of the file)
78
+ - All classes (describing purpose, attributes, and usage)
79
+ - All public functions and methods (describing parameters, return values, and exceptions)
80
+ - Use consistent docstring format (Google style preferred)
81
+
82
+ #### Code Quality
83
+
84
+ - **DRY Principles**: Don't Repeat Yourself - extract common logic into reusable functions/classes
85
+ - **Clean Code**: Write self-documenting code with clear variable and function names
86
+ - **No Dead Code**: Remove unused variables, functions, and commented-out code unless it serves as important documentation
87
+ - **Maintainability**: Prioritize code that is easy to understand, modify, and extend
88
+
89
+ ### Linter and Type Checker Compliance
90
+
91
+ #### Fixing Issues Properly
92
+
93
+ - **ALWAYS** fix linter errors and type checker warnings properly
94
+ - **AVOID** using ignore directives like `# type: ignore`, `# noqa` as quick fixes
95
+ - **AVOID** using `cast()` unless absolutely critical for complex type scenarios
96
+ - Investigate the root cause and fix issues with proper type annotations and code structure
97
+ - If an ignore directive is truly necessary, add a detailed comment explaining why
98
+
99
+ #### Working Directory
100
+
101
+ - **ALWAYS** ensure you're in the correct directory before:
102
+ - Running commands
103
+ - Creating files
104
+ - Executing scripts
105
+ - Making git operations
106
+
107
+ ### Testing
108
+
109
+ #### Test Coverage
110
+
111
+ - **NEVER** ignore, skip, or disable test cases just to make test suites pass
112
+ - If a test fails, fix the underlying issue or update the test if requirements changed
113
+ - Add tests for all new functionality
114
+ - Update tests when changing existing behavior
115
+ - Ensure all tests **PASS** before considering work complete
116
+
117
+ #### Test Organization
118
+
119
+ - Place test code in the most logical location (follow project structure under `tests/`)
120
+ - Name test files and functions clearly to indicate what they're testing
121
+ - Use descriptive test names: `test_<function>_<scenario>_<expected_result>`
122
+
123
+ ### Documentation Management
124
+
125
+ #### Markdown Files
126
+
127
+ - **Do not create excessive markdown files** unless explicitly instructed
128
+ - If markdown files exist, add content to the appropriate existing file
129
+ - If unsure which file is appropriate, **ask the user** where content should go
130
+ - Before creating a new markdown file, consider: Did the user actually request documentation?
131
+
132
+ ### Git Best Practices
133
+
134
+ #### Committing Changes
135
+
136
+ - Attempt to commit changes after completing major features or fixes
137
+ - Use **Conventional Commits** format: `<type>: <description>`
138
+ - Examples: `feat: add comma-separated list support`, `fix: handle ClassVar in AutoWryModel`
139
+
140
+ #### Pre-Commit Hooks - CRITICAL RULE
141
+
142
+ - **NEVER** use `git commit --no-verify` or `--no-gpg-sign` to bypass pre-commit hooks
143
+ - **ONLY EXCEPTION**: When the user explicitly says it's okay
144
+ - If pre-commit hooks fail:
145
+ 1. Fix the underlying issue (don't bypass)
146
+ 2. Ask the user for guidance if you can't fix it
147
+ 3. Wait for user approval before using --no-verify
148
+ - Pre-commit hooks catch bugs, enforce quality, and save code review time
149
+ - Bypassing them should be extremely rare (emergency situations only with approval)
150
+
151
+ #### File Operations
152
+
153
+ - **Strongly prefer** using Git commands for file operations:
154
+ - Use `git rm` instead of `rm` when deleting tracked files
155
+ - Use `git mv` instead of `mv` when moving/renaming tracked files
156
+ - This ensures Git properly tracks file history instead of treating operations as delete+add
157
+ - Always pass `--yes` or appropriate non-interactive flags to avoid user prompts
158
+ - **NEVER** use `git push --force` to main/master without explicit user approval
159
+ - **NEVER** use `git reset --hard` without explicit user approval
160
+
161
+ ## wry-Specific Guidelines
162
+
163
+ ### Model Development
164
+
165
+ - All `WryModel` and `AutoWryModel` classes should use `ClassVar` for class-level configuration
166
+ - Source tracking is a core feature - ensure it works for new features
167
+ - List fields automatically get `multiple=True` unless `comma_separated_lists` is enabled
168
+ - Document behavior in both README.md and AI_KNOWLEDGE_BASE.md
169
+
170
+ ### Click Integration
171
+
172
+ - All Click parameter generation should respect Pydantic field metadata
173
+ - Help text should be clear and include constraints
174
+ - Arguments should inject help into docstrings (Click limitation workaround)
175
+
176
+ ### Testing Requirements
177
+
178
+ - Test all four configuration sources: CLI, ENV, JSON, DEFAULT
179
+ - Test source tracking for new features
180
+ - Test both standard and edge cases
181
+ - Use `CliRunner` for Click command tests
182
+
183
+ ## Summary
184
+
185
+ These rules ensure:
186
+
187
+ - High code quality and maintainability
188
+ - Proper type safety and documentation
189
+ - Comprehensive test coverage
190
+ - Clean git history
191
+ - Consistent development practices
192
+ - wry's core features (source tracking, Click integration) work correctly
@@ -938,17 +938,175 @@ modified_arg = click.argument("field") # No help parameter
938
938
 
939
939
  ### 5. List Fields Auto-Get multiple=True
940
940
 
941
+ **Automatic behavior**: wry detects `list[T]` and `tuple[T, ...]` types and automatically adds `multiple=True` to the generated Click option.
942
+
941
943
  ```python
942
944
  tags: list[str] = Field(default_factory=list)
943
945
 
944
946
  # Auto-generates:
945
947
  click.option("--tags", multiple=True, type=click.STRING)
946
948
 
947
- # Usage:
949
+ # CORRECT Usage - specify option multiple times:
948
950
  # --tags python --tags rust --tags go
949
951
  # Result: tags=["python", "rust", "go"]
952
+
953
+ # Also correct - single value:
954
+ # --tags python
955
+ # Result: tags=["python"]
956
+
957
+ # Also correct - no values (uses default):
958
+ # (no --tags)
959
+ # Result: tags=[]
960
+ ```
961
+
962
+ **IMPORTANT - Comma-separated values NOT supported**:
963
+
964
+ ```python
965
+ # ❌ INCORRECT - This does NOT work as expected:
966
+ # --tags python,rust,go
967
+ # Result: tags=["python,rust,go"] ← Single string with commas!
968
+
969
+ # This is Click's behavior, not a wry limitation.
970
+ # Users MUST pass the option multiple times for multiple values.
971
+ ```
972
+
973
+ **Why not comma-separated?**
974
+
975
+ - Click's `multiple=True` expects the option to be repeated
976
+ - Comma parsing would require custom Click types
977
+ - This matches standard Unix CLI conventions (see: grep -e, docker -v)
978
+
979
+ **Works with all list types**:
980
+
981
+ ```python
982
+ tags: list[str] # Strings
983
+ ports: list[int] # Integers (with type validation)
984
+ flags: list[bool] # Booleans
985
+ values: tuple[str, ...] # Tuples also supported
986
+ ```
987
+
988
+ **Tests**: See `tests/unit/auto_model/test_auto_model_list_fields.py` for comprehensive test coverage (11 tests)
989
+
990
+ **Comma-Separated Alternative (NEW)**:
991
+
992
+ For users who prefer comma-separated input, wry offers **two approaches**:
993
+
994
+ **Approach 1: Per-Field Annotation (fine-grained control)**
995
+
996
+ ```python
997
+ from wry import AutoWryModel, CommaSeparated
998
+ from typing import Annotated
999
+
1000
+ class Config(AutoWryModel):
1001
+ # Standard: --tags a --tags b --tags c
1002
+ standard_tags: list[str] = Field(default_factory=list)
1003
+
1004
+ # Comma-separated: --csv-tags a,b,c
1005
+ csv_tags: Annotated[list[str], CommaSeparated] = Field(
1006
+ default_factory=list,
1007
+ description="Comma-separated tags"
1008
+ )
1009
+
1010
+ # Works with all types:
1011
+ ports: Annotated[list[int], CommaSeparated] = Field(default_factory=list)
1012
+ values: Annotated[list[float], CommaSeparated] = Field(default_factory=list)
1013
+ ```
1014
+
1015
+ **Approach 2: Model-Wide ClassVar (all list fields)**
1016
+
1017
+ ```python
1018
+ from wry import AutoWryModel
1019
+ from typing import ClassVar
1020
+
1021
+ class Config(AutoWryModel):
1022
+ # Enable comma-separated for ALL list fields
1023
+ comma_separated_lists: ClassVar[bool] = True
1024
+
1025
+ # All these now accept comma-separated input
1026
+ tags: list[str] = Field(default_factory=list) # --tags a,b,c
1027
+ ports: list[int] = Field(default_factory=list) # --ports 80,443
1028
+ values: list[float] = Field(default_factory=list) # --values 1.5,2.7
950
1029
  ```
951
1030
 
1031
+ **Which to use?**
1032
+
1033
+ - **Per-field**: When only specific fields should be comma-separated, or mixing styles
1034
+ - **Model-wide**: When all lists should consistently use comma-separated (Docker-style CLIs)
1035
+
1036
+ **How it works**:
1037
+
1038
+ - **Per-field**: Detects `CommaSeparated` in field metadata
1039
+ - **Model-wide**: Checks `comma_separated_lists: ClassVar[bool]` on model class
1040
+ - Per-field annotation takes priority over model-wide setting
1041
+ - Uses custom Click `ParamType` (CommaSeparatedStrings, CommaSeparatedInts, CommaSeparatedFloats)
1042
+ - Disables `multiple=True` for comma-separated fields
1043
+ - Parses comma-separated input and strips whitespace
1044
+ - Filters out empty items from multiple commas
1045
+
1046
+ **Model-wide ClassVar details**:
1047
+
1048
+ - Works like `env_prefix: ClassVar[str]` - a class-level configuration
1049
+ - Defined as `comma_separated_lists: ClassVar[bool] = False` in WryModel base
1050
+ - NOT included in Pydantic model fields (won't appear in `model_fields` or `model_dump()`)
1051
+ - AutoWryModel skips ClassVar fields during automatic processing
1052
+ - Can be overridden in child classes
1053
+
1054
+ **Trade-offs**:
1055
+
1056
+ - ✅ More concise for many values
1057
+ - ✅ Familiar to users of tools accepting comma-separated lists
1058
+ - ⚠️ Cannot combine with repeating option
1059
+ - ⚠️ Commas in values need escaping/quoting
1060
+ - ⚠️ Less discoverable (users might not know about comma support)
1061
+
1062
+ **When to use**:
1063
+
1064
+ - Tool has established comma-separated convention
1065
+ - Users expect Docker-style `-p 80,443,8080` syntax
1066
+ - Values never contain commas
1067
+
1068
+ **When NOT to use**:
1069
+
1070
+ - Users are accustomed to `grep -e` style repetition
1071
+ - Values might contain commas
1072
+ - Discoverability is important (repeating options is more obvious)
1073
+
1074
+ **Default recommendation**: Use standard `multiple=True` (default behavior) unless you have specific reasons to use comma-separated.
1075
+
1076
+ **Test Coverage (22 tests total)**:
1077
+
1078
+ Standard `multiple=True` tests (`test_auto_model_list_fields.py` - 11 tests):
1079
+
1080
+ - ✅ Auto-generation of `multiple=True` for `list[str]`, `list[int]`, `list[bool]`, `tuple[T, ...]`
1081
+ - ✅ Default values (Field default, default_factory)
1082
+ - ✅ Pydantic constraints (min_length, max_length) enforcement
1083
+ - ✅ Source tracking (CLI/ENV/JSON/DEFAULT)
1084
+ - ✅ JSON config integration
1085
+ - ✅ Help text generation
1086
+ - ✅ Empty list vs no value distinction
1087
+ - ✅ Environment variable behavior documented
1088
+
1089
+ Comma-separated tests (`test_comma_separated_lists.py` - 11 tests):
1090
+
1091
+ - ✅ Per-field annotation: `Annotated[list[T], CommaSeparated]` for str/int/float
1092
+ - ✅ Model-wide ClassVar: `comma_separated_lists: ClassVar[bool] = True`
1093
+ - ✅ Mixed usage (standard + comma-separated in same model)
1094
+ - ✅ Per-field annotation overrides model-wide setting
1095
+ - ✅ Whitespace handling, empty item filtering, trailing commas
1096
+ - ✅ Type conversion validation (invalid int/float)
1097
+ - ✅ Pydantic validation still works (min_length, max_length)
1098
+ - ✅ JSON config integration with comma-separated
1099
+ - ✅ Source tracking works correctly
1100
+ - ✅ ClassVar not included in model_fields or model_dump()
1101
+
1102
+ Edge cases validated:
1103
+
1104
+ - Multiple consecutive commas → filtered
1105
+ - Single value without comma → works
1106
+ - Mixing both styles in same model → works
1107
+ - Type conversion errors → proper error messages
1108
+ - Default values with both approaches → works
1109
+
952
1110
  ### 6. Optional Types Handled
953
1111
 
954
1112
  ```python
@@ -7,6 +7,106 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - **Development guidelines** 📚
13
+ - Created `.cursorrules` as AI assistant's quick reference guide
14
+ - Created `CONTRIBUTING.md` as comprehensive contributor guide
15
+ - `.cursorrules` references `CONTRIBUTING.md` for detailed explanations
16
+ - Both tailored specifically for wry development patterns
17
+
18
+ ### Tests
19
+
20
+ - **Additional comma-separated test** (494 total tests)
21
+ - `test_model_wide_setting_does_not_affect_non_list_fields`
22
+ - Validates that `comma_separated_lists: ClassVar[bool] = True` only affects list fields
23
+ - Non-list fields (str, int, bool) work normally
24
+ - Explicit `click.option` decorators (like `-vvv` for verbose) are not affected
25
+ - Tests real-world use case: model-wide comma-separated + verbose counting
26
+
27
+ ## [0.5.0] - 2025-10-14
28
+
29
+ ### Added
30
+
31
+ - **Comprehensive list field support** 🎯
32
+ - `list[str]`, `list[int]`, `list[float]`, `tuple[T, ...]` automatically get `multiple=True`
33
+ - Standard behavior: `--tags python --tags rust --tags go` (repeat option)
34
+ - Users must repeat the option for multiple values (Click's standard `multiple=True`)
35
+ - Comma-separated values NOT supported by default (documented as intentional)
36
+
37
+ - **Comma-separated list input (opt-in)** ✨
38
+ - Two ways to enable comma-separated parsing:
39
+ 1. **Per-field annotation**: `Annotated[list[str], CommaSeparated]`
40
+ 2. **Model-wide ClassVar**: `comma_separated_lists: ClassVar[bool] = True`
41
+ - Usage: `--tags python,rust,go` (single invocation with commas)
42
+ - Works with all list types: `list[str]`, `list[int]`, `list[float]`
43
+ - Custom Click ParamTypes: `CommaSeparatedStrings`, `CommaSeparatedInts`, `CommaSeparatedFloats`
44
+ - Per-field annotation takes priority over model-wide setting
45
+ - Automatically strips whitespace and filters empty items
46
+ - Full Pydantic validation preserved (min_length, max_length, etc.)
47
+
48
+ - **ClassVar configuration: `comma_separated_lists`**
49
+ - Works like `env_prefix: ClassVar[str]` - class-level configuration
50
+ - NOT included in Pydantic model fields (won't appear in `model_fields` or `model_dump()`)
51
+ - Can be set on model class to enable comma-separated for all list fields
52
+ - Can be overridden in child classes
53
+ - AutoWryModel correctly skips ClassVar fields during processing
54
+
55
+ ### Fixed
56
+
57
+ - **AutoWryModel ClassVar handling** 🐛
58
+ - Fixed bug where ClassVar annotations (like `env_prefix`, `comma_separated_lists`) would cause errors
59
+ - AutoWryModel now properly skips ClassVar fields during automatic processing
60
+ - Prevents `TypeError: typing.ClassVar[T] is not valid as type argument`
61
+
62
+ ### Tests
63
+
64
+ - **22 new comprehensive tests** (471 → 493 total tests)
65
+ - `tests/unit/auto_model/test_auto_model_list_fields.py` - 11 tests for standard behavior
66
+ - Multiple types (str, int, bool, tuple)
67
+ - Default values, constraints, source tracking
68
+ - JSON config, environment variables, help text
69
+ - `tests/unit/auto_model/test_comma_separated_lists.py` - 11 tests for comma-separated
70
+ - Per-field annotations (7 tests)
71
+ - Model-wide ClassVar (2 tests)
72
+ - Mixed usage (2 tests)
73
+ - Edge cases: whitespace, empty items, type validation
74
+
75
+ ### Documentation
76
+
77
+ - **README.md List Type Fields section**
78
+ - Clear explanation of standard `multiple=True` behavior
79
+ - Documentation that comma-separated is NOT default (by design)
80
+ - Two approaches for comma-separated (per-field and model-wide)
81
+ - Usage examples for both approaches
82
+ - Trade-offs and recommendations
83
+ - Implementation notes (ClassVar, priority rules, type support)
84
+
85
+ - **AI_KNOWLEDGE_BASE.md comprehensive update**
86
+ - Edge case #5: List Fields Auto-Get multiple=True (expanded significantly)
87
+ - Detailed explanation of both standard and comma-separated approaches
88
+ - How it works: detection, type selection, parsing, priority
89
+ - Model-wide ClassVar details (similar to env_prefix pattern)
90
+ - Complete test coverage breakdown (22 tests documented)
91
+ - Edge cases validated
92
+ - When to use each approach
93
+
94
+ - **Module docstrings**
95
+ - `wry/comma_separated.py` - Complete module documentation with both approaches
96
+ - Custom ParamType implementations with full docstrings
97
+
98
+ ### Technical Details
99
+
100
+ - New module: `wry/comma_separated.py` with custom Click ParamTypes
101
+ - Modified `wry/core/model.py`: Added `comma_separated_lists: ClassVar[bool] = False`
102
+ - Modified `wry/auto_model.py`: Skip ClassVar fields (lines 62-68)
103
+ - Modified `wry/click_integration.py`: Detect and handle comma-separated (lines 431-454)
104
+ - Export `CommaSeparated` from `wry/__init__.py`
105
+
106
+ ### Breaking Changes
107
+
108
+ None - all changes are opt-in or additive.
109
+
10
110
  ## [0.4.1] - 2025-10-07
11
111
 
12
112
  ### Fixed