pyopenapi-gen 0.8.7__py3-none-any.whl → 0.10.1__py3-none-any.whl
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.
- pyopenapi_gen/__init__.py +2 -2
- pyopenapi_gen/context/CLAUDE.md +284 -0
- pyopenapi_gen/context/import_collector.py +8 -8
- pyopenapi_gen/core/CLAUDE.md +224 -0
- pyopenapi_gen/core/loader/operations/parser.py +1 -1
- pyopenapi_gen/core/parsing/cycle_helpers.py +1 -1
- pyopenapi_gen/core/parsing/keywords/properties_parser.py +4 -4
- pyopenapi_gen/core/parsing/schema_parser.py +4 -4
- pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +1 -1
- pyopenapi_gen/core/writers/python_construct_renderer.py +2 -2
- pyopenapi_gen/emitters/CLAUDE.md +286 -0
- pyopenapi_gen/emitters/endpoints_emitter.py +1 -1
- pyopenapi_gen/generator/CLAUDE.md +352 -0
- pyopenapi_gen/helpers/CLAUDE.md +325 -0
- pyopenapi_gen/helpers/endpoint_utils.py +2 -2
- pyopenapi_gen/helpers/type_cleaner.py +1 -1
- pyopenapi_gen/helpers/type_resolution/composition_resolver.py +1 -1
- pyopenapi_gen/helpers/type_resolution/finalizer.py +1 -1
- pyopenapi_gen/types/CLAUDE.md +140 -0
- pyopenapi_gen/types/resolvers/schema_resolver.py +2 -2
- pyopenapi_gen/visit/CLAUDE.md +272 -0
- pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +1 -1
- pyopenapi_gen/visit/endpoint/generators/signature_generator.py +1 -1
- pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +1 -1
- {pyopenapi_gen-0.8.7.dist-info → pyopenapi_gen-0.10.1.dist-info}/METADATA +18 -4
- {pyopenapi_gen-0.8.7.dist-info → pyopenapi_gen-0.10.1.dist-info}/RECORD +29 -22
- {pyopenapi_gen-0.8.7.dist-info → pyopenapi_gen-0.10.1.dist-info}/WHEEL +0 -0
- {pyopenapi_gen-0.8.7.dist-info → pyopenapi_gen-0.10.1.dist-info}/entry_points.txt +0 -0
- {pyopenapi_gen-0.8.7.dist-info → pyopenapi_gen-0.10.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,325 @@
|
|
1
|
+
# helpers/ - Legacy Compatibility Layer
|
2
|
+
|
3
|
+
## Why This Folder?
|
4
|
+
Backward compatibility during transition to unified type system. Delegates to new `types/` system while maintaining old API surface for gradual migration.
|
5
|
+
|
6
|
+
## Key Dependencies
|
7
|
+
- **Delegates to**: `../types/services/UnifiedTypeService`
|
8
|
+
- **Used by**: Legacy code that hasn't migrated to unified system
|
9
|
+
- **Status**: Transitional - prefer `types/` for new code
|
10
|
+
|
11
|
+
## Critical Architecture
|
12
|
+
|
13
|
+
### 1. Legacy → Unified Delegation
|
14
|
+
```python
|
15
|
+
# type_helper.py
|
16
|
+
class TypeHelper:
|
17
|
+
@staticmethod
|
18
|
+
def get_python_type_for_schema(schema: IRSchema, all_schemas: Dict[str, IRSchema],
|
19
|
+
context: RenderContext, required: bool = True,
|
20
|
+
resolve_alias_target: bool = False) -> str:
|
21
|
+
"""Legacy API - delegates to UnifiedTypeService"""
|
22
|
+
type_service = UnifiedTypeService(all_schemas)
|
23
|
+
return type_service.resolve_schema_type(
|
24
|
+
schema, context, required, resolve_underlying=resolve_alias_target
|
25
|
+
)
|
26
|
+
```
|
27
|
+
|
28
|
+
### 2. Type Resolution Subdirectory
|
29
|
+
```python
|
30
|
+
# type_resolution/ - Legacy individual resolvers
|
31
|
+
# These now delegate to unified system components
|
32
|
+
array_resolver.py → types/resolvers/schema_resolver.py
|
33
|
+
composition_resolver.py → types/resolvers/schema_resolver.py
|
34
|
+
object_resolver.py → types/resolvers/schema_resolver.py
|
35
|
+
primitive_resolver.py → types/resolvers/schema_resolver.py
|
36
|
+
```
|
37
|
+
|
38
|
+
## Migration Strategy
|
39
|
+
|
40
|
+
### 1. Deprecation Pattern
|
41
|
+
```python
|
42
|
+
import warnings
|
43
|
+
from ..types.services import UnifiedTypeService
|
44
|
+
|
45
|
+
def legacy_function(schema: IRSchema, context: RenderContext) -> str:
|
46
|
+
"""
|
47
|
+
DEPRECATED: Use UnifiedTypeService.resolve_schema_type() instead.
|
48
|
+
This function will be removed in version 2.0.
|
49
|
+
"""
|
50
|
+
warnings.warn(
|
51
|
+
"legacy_function is deprecated. Use UnifiedTypeService.resolve_schema_type()",
|
52
|
+
DeprecationWarning,
|
53
|
+
stacklevel=2
|
54
|
+
)
|
55
|
+
|
56
|
+
# Delegate to new system
|
57
|
+
type_service = UnifiedTypeService({})
|
58
|
+
return type_service.resolve_schema_type(schema, context)
|
59
|
+
```
|
60
|
+
|
61
|
+
### 2. API Compatibility
|
62
|
+
```python
|
63
|
+
# Maintain old signatures while delegating
|
64
|
+
def get_return_type(operation: IROperation, context: RenderContext,
|
65
|
+
schemas: Dict[str, IRSchema]) -> Tuple[str, bool]:
|
66
|
+
"""Legacy endpoint_utils function"""
|
67
|
+
type_service = UnifiedTypeService(schemas)
|
68
|
+
return type_service.resolve_operation_response_with_unwrap_info(operation, context)
|
69
|
+
```
|
70
|
+
|
71
|
+
## Critical Components
|
72
|
+
|
73
|
+
### type_helper.py
|
74
|
+
**Purpose**: Main legacy entry point for type resolution
|
75
|
+
```python
|
76
|
+
class TypeHelper:
|
77
|
+
@staticmethod
|
78
|
+
def get_python_type_for_schema(schema: IRSchema, all_schemas: Dict[str, IRSchema],
|
79
|
+
context: RenderContext, required: bool = True,
|
80
|
+
resolve_alias_target: bool = False) -> str:
|
81
|
+
"""
|
82
|
+
Legacy type resolution - delegates to UnifiedTypeService
|
83
|
+
|
84
|
+
Args:
|
85
|
+
schema: Schema to resolve
|
86
|
+
all_schemas: All schemas in spec (for references)
|
87
|
+
context: Render context for imports
|
88
|
+
required: Whether field is required (affects Optional[])
|
89
|
+
resolve_alias_target: Whether to resolve through aliases
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Python type string
|
93
|
+
"""
|
94
|
+
type_service = UnifiedTypeService(all_schemas)
|
95
|
+
return type_service.resolve_schema_type(
|
96
|
+
schema, context, required, resolve_underlying=resolve_alias_target
|
97
|
+
)
|
98
|
+
```
|
99
|
+
|
100
|
+
### endpoint_utils.py
|
101
|
+
**Purpose**: Legacy endpoint-specific utilities
|
102
|
+
```python
|
103
|
+
def get_return_type(operation: IROperation, context: RenderContext,
|
104
|
+
schemas: Dict[str, IRSchema]) -> Tuple[str, bool]:
|
105
|
+
"""
|
106
|
+
Legacy function for getting operation return type
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
Tuple of (python_type, was_unwrapped)
|
110
|
+
"""
|
111
|
+
type_service = UnifiedTypeService(schemas)
|
112
|
+
return type_service.resolve_operation_response_with_unwrap_info(operation, context)
|
113
|
+
|
114
|
+
def get_endpoint_return_types(operation: IROperation, context: RenderContext,
|
115
|
+
schemas: Dict[str, IRSchema]) -> Dict[str, str]:
|
116
|
+
"""Legacy function for getting all response types"""
|
117
|
+
type_service = UnifiedTypeService(schemas)
|
118
|
+
return type_service.resolve_all_response_types(operation, context)
|
119
|
+
```
|
120
|
+
|
121
|
+
### type_resolution/ Subdirectory
|
122
|
+
**Purpose**: Legacy individual type resolvers
|
123
|
+
|
124
|
+
#### array_resolver.py
|
125
|
+
```python
|
126
|
+
def resolve_array_type(schema: IRSchema, context: RenderContext,
|
127
|
+
all_schemas: Dict[str, IRSchema]) -> str:
|
128
|
+
"""Legacy array type resolution"""
|
129
|
+
type_service = UnifiedTypeService(all_schemas)
|
130
|
+
return type_service.resolve_schema_type(schema, context)
|
131
|
+
```
|
132
|
+
|
133
|
+
#### composition_resolver.py
|
134
|
+
```python
|
135
|
+
def resolve_composition_type(schema: IRSchema, context: RenderContext,
|
136
|
+
all_schemas: Dict[str, IRSchema]) -> str:
|
137
|
+
"""Legacy composition (allOf/oneOf/anyOf) resolution"""
|
138
|
+
type_service = UnifiedTypeService(all_schemas)
|
139
|
+
return type_service.resolve_schema_type(schema, context)
|
140
|
+
```
|
141
|
+
|
142
|
+
## Usage Patterns (Legacy)
|
143
|
+
|
144
|
+
### 1. Type Resolution
|
145
|
+
```python
|
146
|
+
# OLD WAY (still works, but deprecated)
|
147
|
+
from pyopenapi_gen.helpers.type_helper import TypeHelper
|
148
|
+
|
149
|
+
python_type = TypeHelper.get_python_type_for_schema(
|
150
|
+
schema, all_schemas, context, required=True
|
151
|
+
)
|
152
|
+
|
153
|
+
# NEW WAY (preferred)
|
154
|
+
from pyopenapi_gen.types.services import UnifiedTypeService
|
155
|
+
|
156
|
+
type_service = UnifiedTypeService(all_schemas)
|
157
|
+
python_type = type_service.resolve_schema_type(schema, context, required=True)
|
158
|
+
```
|
159
|
+
|
160
|
+
### 2. Endpoint Type Resolution
|
161
|
+
```python
|
162
|
+
# OLD WAY (still works, but deprecated)
|
163
|
+
from pyopenapi_gen.helpers.endpoint_utils import get_return_type
|
164
|
+
|
165
|
+
return_type, was_unwrapped = get_return_type(operation, context, schemas)
|
166
|
+
|
167
|
+
# NEW WAY (preferred)
|
168
|
+
from pyopenapi_gen.types.services import UnifiedTypeService
|
169
|
+
|
170
|
+
type_service = UnifiedTypeService(schemas)
|
171
|
+
return_type, was_unwrapped = type_service.resolve_operation_response_with_unwrap_info(
|
172
|
+
operation, context
|
173
|
+
)
|
174
|
+
```
|
175
|
+
|
176
|
+
## Migration Guide
|
177
|
+
|
178
|
+
### 1. Type Helper Migration
|
179
|
+
```python
|
180
|
+
# Before
|
181
|
+
from pyopenapi_gen.helpers.type_helper import TypeHelper
|
182
|
+
|
183
|
+
class MyVisitor:
|
184
|
+
def resolve_type(self, schema: IRSchema) -> str:
|
185
|
+
return TypeHelper.get_python_type_for_schema(
|
186
|
+
schema, self.all_schemas, self.context, required=True
|
187
|
+
)
|
188
|
+
|
189
|
+
# After
|
190
|
+
from pyopenapi_gen.types.services import UnifiedTypeService
|
191
|
+
|
192
|
+
class MyVisitor:
|
193
|
+
def __init__(self, all_schemas: Dict[str, IRSchema]):
|
194
|
+
self.type_service = UnifiedTypeService(all_schemas)
|
195
|
+
|
196
|
+
def resolve_type(self, schema: IRSchema) -> str:
|
197
|
+
return self.type_service.resolve_schema_type(
|
198
|
+
schema, self.context, required=True
|
199
|
+
)
|
200
|
+
```
|
201
|
+
|
202
|
+
### 2. Endpoint Utils Migration
|
203
|
+
```python
|
204
|
+
# Before
|
205
|
+
from pyopenapi_gen.helpers.endpoint_utils import get_return_type
|
206
|
+
|
207
|
+
def generate_method(self, operation: IROperation):
|
208
|
+
return_type, was_unwrapped = get_return_type(operation, self.context, self.schemas)
|
209
|
+
|
210
|
+
# After
|
211
|
+
from pyopenapi_gen.types.services import UnifiedTypeService
|
212
|
+
|
213
|
+
def generate_method(self, operation: IROperation):
|
214
|
+
return_type, was_unwrapped = self.type_service.resolve_operation_response_with_unwrap_info(
|
215
|
+
operation, self.context
|
216
|
+
)
|
217
|
+
```
|
218
|
+
|
219
|
+
## Testing Strategy
|
220
|
+
|
221
|
+
### 1. Compatibility Tests
|
222
|
+
```python
|
223
|
+
def test_type_helper__legacy_api__matches_unified_service():
|
224
|
+
"""Ensure legacy API produces same results as unified service"""
|
225
|
+
|
226
|
+
# Test with legacy API
|
227
|
+
legacy_result = TypeHelper.get_python_type_for_schema(
|
228
|
+
schema, all_schemas, context, required=True
|
229
|
+
)
|
230
|
+
|
231
|
+
# Test with unified service
|
232
|
+
type_service = UnifiedTypeService(all_schemas)
|
233
|
+
unified_result = type_service.resolve_schema_type(
|
234
|
+
schema, context, required=True
|
235
|
+
)
|
236
|
+
|
237
|
+
assert legacy_result == unified_result
|
238
|
+
```
|
239
|
+
|
240
|
+
### 2. Deprecation Warning Tests
|
241
|
+
```python
|
242
|
+
def test_legacy_function__emits_deprecation_warning():
|
243
|
+
"""Ensure legacy functions emit deprecation warnings"""
|
244
|
+
|
245
|
+
with warnings.catch_warnings(record=True) as w:
|
246
|
+
warnings.simplefilter("always")
|
247
|
+
|
248
|
+
# Call legacy function
|
249
|
+
TypeHelper.get_python_type_for_schema(schema, all_schemas, context)
|
250
|
+
|
251
|
+
# Check warning was emitted
|
252
|
+
assert len(w) == 1
|
253
|
+
assert issubclass(w[0].category, DeprecationWarning)
|
254
|
+
assert "deprecated" in str(w[0].message)
|
255
|
+
```
|
256
|
+
|
257
|
+
## Removal Timeline
|
258
|
+
|
259
|
+
### Phase 1: Deprecation (Current)
|
260
|
+
- Add deprecation warnings to all legacy functions
|
261
|
+
- Update internal code to use unified system
|
262
|
+
- Maintain backward compatibility
|
263
|
+
|
264
|
+
### Phase 2: Migration (Future)
|
265
|
+
- Remove legacy functions
|
266
|
+
- Update all external references
|
267
|
+
- Remove helpers/ directory
|
268
|
+
|
269
|
+
## Extension Points
|
270
|
+
|
271
|
+
### Custom Legacy Adapters
|
272
|
+
```python
|
273
|
+
class CustomLegacyAdapter:
|
274
|
+
"""Adapt old custom APIs to unified system"""
|
275
|
+
|
276
|
+
def __init__(self, type_service: UnifiedTypeService):
|
277
|
+
self.type_service = type_service
|
278
|
+
|
279
|
+
def old_custom_method(self, schema: IRSchema) -> str:
|
280
|
+
# Convert old API to new unified service call
|
281
|
+
return self.type_service.resolve_schema_type(schema, context)
|
282
|
+
```
|
283
|
+
|
284
|
+
## Critical Implementation Details
|
285
|
+
|
286
|
+
### Error Handling
|
287
|
+
```python
|
288
|
+
def legacy_function(schema: IRSchema) -> str:
|
289
|
+
"""Legacy function with error handling"""
|
290
|
+
try:
|
291
|
+
# Delegate to unified system
|
292
|
+
return unified_function(schema)
|
293
|
+
except Exception as e:
|
294
|
+
# Convert unified errors to legacy error format
|
295
|
+
raise LegacyError(f"Legacy function failed: {e}")
|
296
|
+
```
|
297
|
+
|
298
|
+
### Performance Considerations
|
299
|
+
```python
|
300
|
+
# Cache UnifiedTypeService instances to avoid recreation
|
301
|
+
_type_service_cache = {}
|
302
|
+
|
303
|
+
def get_cached_type_service(schemas: Dict[str, IRSchema]) -> UnifiedTypeService:
|
304
|
+
"""Get cached type service for performance"""
|
305
|
+
schema_hash = hash(frozenset(schemas.keys()))
|
306
|
+
|
307
|
+
if schema_hash not in _type_service_cache:
|
308
|
+
_type_service_cache[schema_hash] = UnifiedTypeService(schemas)
|
309
|
+
|
310
|
+
return _type_service_cache[schema_hash]
|
311
|
+
```
|
312
|
+
|
313
|
+
## Common Pitfalls
|
314
|
+
|
315
|
+
1. **Direct Usage**: Using legacy functions in new code
|
316
|
+
2. **Missing Warnings**: Not emitting deprecation warnings
|
317
|
+
3. **Inconsistent Results**: Legacy and unified APIs returning different results
|
318
|
+
4. **Performance**: Creating new UnifiedTypeService instances repeatedly
|
319
|
+
|
320
|
+
## Best Practices
|
321
|
+
|
322
|
+
1. **Prefer Unified System**: Use `types/` for all new code
|
323
|
+
2. **Emit Warnings**: Always emit deprecation warnings in legacy functions
|
324
|
+
3. **Test Compatibility**: Ensure legacy and unified APIs return same results
|
325
|
+
4. **Document Migration**: Provide clear migration paths in docstrings
|
@@ -308,10 +308,10 @@ def format_method_args(params: list[dict[str, Any]]) -> str:
|
|
308
308
|
optional = [p for p in params if not p.get("required", True)]
|
309
309
|
arg_strs = []
|
310
310
|
for p in required:
|
311
|
-
arg_strs.append(f"{p[
|
311
|
+
arg_strs.append(f"{p['name']}: {p['type']}")
|
312
312
|
for p in optional:
|
313
313
|
default = p["default"]
|
314
|
-
arg_strs.append(f"{p[
|
314
|
+
arg_strs.append(f"{p['name']}: {p['type']} = {default}")
|
315
315
|
return ", ".join(arg_strs)
|
316
316
|
|
317
317
|
|
@@ -220,7 +220,7 @@ class TypeCleaner:
|
|
220
220
|
if len(unique_members) == 1:
|
221
221
|
return unique_members[0] # A Union with one member is just that member.
|
222
222
|
|
223
|
-
return f"Union[{
|
223
|
+
return f"Union[{', '.join(unique_members)}]"
|
224
224
|
|
225
225
|
@classmethod
|
226
226
|
def _clean_list_type(cls, type_str: str) -> str:
|
@@ -23,7 +23,7 @@ class TypeFinalizer:
|
|
23
23
|
if py_type is None:
|
24
24
|
logger.warning(
|
25
25
|
f"[TypeFinalizer] Received None as py_type for schema "
|
26
|
-
f"'{schema.name or
|
26
|
+
f"'{schema.name or 'anonymous'}'. Defaulting to 'Any'."
|
27
27
|
)
|
28
28
|
self.context.add_import("typing", "Any")
|
29
29
|
py_type = "Any"
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# types/ - Unified Type Resolution System
|
2
|
+
|
3
|
+
## Why This Folder?
|
4
|
+
Central, testable type resolution replacing scattered type conversion logic. Single source of truth for OpenAPI → Python type mappings with dependency injection architecture.
|
5
|
+
|
6
|
+
## Key Dependencies
|
7
|
+
- **Input**: `IRSchema`, `IRResponse`, `IROperation` from `../../ir.py`
|
8
|
+
- **Output**: Python type strings (`str`, `List[User]`, `Optional[Dict[str, Any]]`)
|
9
|
+
- **Context**: `RenderContext` from `../../context/render_context.py`
|
10
|
+
|
11
|
+
## Essential Patterns
|
12
|
+
|
13
|
+
### 1. Service → Resolvers → Contracts
|
14
|
+
```python
|
15
|
+
# Main entry point (services/)
|
16
|
+
UnifiedTypeService → SchemaResolver/ResponseResolver → TypeContext protocol
|
17
|
+
|
18
|
+
# Usage pattern
|
19
|
+
type_service = UnifiedTypeService(schemas, responses)
|
20
|
+
python_type = type_service.resolve_schema_type(schema, context, required=True)
|
21
|
+
```
|
22
|
+
|
23
|
+
### 2. Protocol-Based Dependency Injection
|
24
|
+
```python
|
25
|
+
# contracts/protocols.py - Define interfaces
|
26
|
+
class TypeContext(Protocol):
|
27
|
+
def add_import(self, import_str: str) -> None: ...
|
28
|
+
|
29
|
+
# resolvers/ - Use protocols, not concrete types
|
30
|
+
def resolve_type(schema: IRSchema, context: TypeContext) -> str:
|
31
|
+
# Implementation uses context protocol
|
32
|
+
```
|
33
|
+
|
34
|
+
### 3. Error Handling
|
35
|
+
```python
|
36
|
+
from .contracts.types import TypeResolutionError
|
37
|
+
|
38
|
+
# Always wrap resolution failures
|
39
|
+
try:
|
40
|
+
return resolve_complex_type(schema)
|
41
|
+
except Exception as e:
|
42
|
+
raise TypeResolutionError(f"Failed to resolve {schema.name}: {e}")
|
43
|
+
```
|
44
|
+
|
45
|
+
## Critical Implementation Details
|
46
|
+
|
47
|
+
### Schema Type Resolution Priority
|
48
|
+
1. **Enum**: `schema.enum` → `UserStatusEnum`
|
49
|
+
2. **Named Reference**: `schema.type` as schema name → `User`
|
50
|
+
3. **Primitive**: `schema.type` → `str`, `int`, `bool`
|
51
|
+
4. **Array**: `schema.type="array"` → `List[ItemType]`
|
52
|
+
5. **Object**: `schema.type="object"` → `Dict[str, Any]` or dataclass
|
53
|
+
6. **Composition**: `allOf`/`oneOf`/`anyOf` → `Union[...]`
|
54
|
+
|
55
|
+
### Forward Reference Handling
|
56
|
+
```python
|
57
|
+
# For circular dependencies
|
58
|
+
if schema.name in context.forward_refs:
|
59
|
+
return f'"{schema.name}"' # String annotation
|
60
|
+
```
|
61
|
+
|
62
|
+
### Response Unwrapping Logic
|
63
|
+
```python
|
64
|
+
# Detect wrapper responses with single 'data' field
|
65
|
+
if (response.schema.type == "object" and
|
66
|
+
"data" in response.schema.properties and
|
67
|
+
len(response.schema.properties) == 1):
|
68
|
+
return resolve_schema_type(response.schema.properties["data"])
|
69
|
+
```
|
70
|
+
|
71
|
+
## Dependencies on Other Systems
|
72
|
+
|
73
|
+
### From core/
|
74
|
+
- `IRSchema`, `IRResponse`, `IROperation` definitions
|
75
|
+
- Parsing context for cycle detection state
|
76
|
+
|
77
|
+
### From context/
|
78
|
+
- `RenderContext` for import management and rendering state
|
79
|
+
- Import collection and deduplication
|
80
|
+
|
81
|
+
### From helpers/ (Legacy)
|
82
|
+
- `TypeHelper` delegates to `UnifiedTypeService`
|
83
|
+
- Maintains backward compatibility during transition
|
84
|
+
|
85
|
+
## Testing Requirements
|
86
|
+
|
87
|
+
### Unit Test Pattern
|
88
|
+
```python
|
89
|
+
def test_resolve_schema_type__string_schema__returns_str():
|
90
|
+
# Arrange
|
91
|
+
schema = IRSchema(type="string")
|
92
|
+
mock_context = Mock(spec=TypeContext)
|
93
|
+
resolver = OpenAPISchemaResolver({})
|
94
|
+
|
95
|
+
# Act
|
96
|
+
result = resolver.resolve_type(schema, mock_context)
|
97
|
+
|
98
|
+
# Assert
|
99
|
+
assert result == "str"
|
100
|
+
```
|
101
|
+
|
102
|
+
### Integration Test Pattern
|
103
|
+
```python
|
104
|
+
def test_type_service__complex_schema__resolves_correctly():
|
105
|
+
# Test with real schemas and context
|
106
|
+
schemas = {"User": IRSchema(...)}
|
107
|
+
responses = {"UserResponse": IRResponse(...)}
|
108
|
+
service = UnifiedTypeService(schemas, responses)
|
109
|
+
# Test actual resolution
|
110
|
+
```
|
111
|
+
|
112
|
+
## Common Pitfalls
|
113
|
+
|
114
|
+
1. **Context Mutation**: Always pass context, never mutate globally
|
115
|
+
2. **Missing Imports**: Resolver must call `context.add_import()` for complex types
|
116
|
+
3. **Circular Dependencies**: Check `context.forward_refs` before resolution
|
117
|
+
4. **Error Swallowing**: Wrap exceptions in `TypeResolutionError`
|
118
|
+
|
119
|
+
## Extension Points
|
120
|
+
|
121
|
+
### Adding New Resolvers
|
122
|
+
```python
|
123
|
+
# Create new resolver implementing protocols
|
124
|
+
class CustomResolver:
|
125
|
+
def resolve_type(self, schema: IRSchema, context: TypeContext) -> str:
|
126
|
+
# Custom logic
|
127
|
+
pass
|
128
|
+
|
129
|
+
# Register in UnifiedTypeService
|
130
|
+
service.register_resolver(CustomResolver())
|
131
|
+
```
|
132
|
+
|
133
|
+
### New Response Strategies
|
134
|
+
```python
|
135
|
+
# strategies/response_strategy.py
|
136
|
+
class CustomResponseStrategy:
|
137
|
+
def should_unwrap(self, response: IRResponse) -> bool:
|
138
|
+
# Custom unwrapping logic
|
139
|
+
pass
|
140
|
+
```
|
@@ -307,7 +307,7 @@ class OpenAPISchemaResolver(SchemaTypeResolver):
|
|
307
307
|
# Sort types for consistent ordering
|
308
308
|
resolved_types.sort()
|
309
309
|
context.add_import("typing", "Union")
|
310
|
-
union_type = f"Union[{
|
310
|
+
union_type = f"Union[{', '.join(resolved_types)}]"
|
311
311
|
|
312
312
|
return ResolvedType(python_type=union_type, is_optional=not required)
|
313
313
|
|
@@ -362,6 +362,6 @@ class OpenAPISchemaResolver(SchemaTypeResolver):
|
|
362
362
|
# Sort types for consistent ordering
|
363
363
|
resolved_types.sort()
|
364
364
|
context.add_import("typing", "Union")
|
365
|
-
union_type = f"Union[{
|
365
|
+
union_type = f"Union[{', '.join(resolved_types)}]"
|
366
366
|
|
367
367
|
return ResolvedType(python_type=union_type, is_optional=not required)
|