pyopenapi-gen 0.11.0__py3-none-any.whl → 0.12.0__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/core/loader/loader.py +4 -2
- pyopenapi_gen/core/loader/operations/parser.py +12 -6
- pyopenapi_gen/core/loader/operations/post_processor.py +4 -2
- pyopenapi_gen/core/loader/operations/request_body.py +10 -5
- pyopenapi_gen/core/loader/parameters/parser.py +43 -10
- pyopenapi_gen/core/loader/responses/parser.py +14 -7
- pyopenapi_gen/core/loader/schemas/extractor.py +25 -11
- pyopenapi_gen/core/parsing/context.py +4 -2
- pyopenapi_gen/core/parsing/keywords/all_of_parser.py +8 -4
- pyopenapi_gen/core/parsing/keywords/any_of_parser.py +10 -5
- pyopenapi_gen/core/parsing/keywords/array_items_parser.py +6 -3
- pyopenapi_gen/core/parsing/keywords/one_of_parser.py +10 -5
- pyopenapi_gen/core/parsing/schema_finalizer.py +6 -3
- pyopenapi_gen/core/parsing/schema_parser.py +6 -4
- pyopenapi_gen/emitters/endpoints_emitter.py +2 -1
- pyopenapi_gen/emitters/models_emitter.py +12 -14
- pyopenapi_gen/helpers/type_resolution/named_resolver.py +6 -7
- pyopenapi_gen/helpers/type_resolution/object_resolver.py +18 -14
- pyopenapi_gen/types/resolvers/schema_resolver.py +9 -2
- pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +1 -1
- pyopenapi_gen/visit/model/alias_generator.py +15 -8
- pyopenapi_gen/visit/model/dataclass_generator.py +25 -15
- pyopenapi_gen/visit/model/enum_generator.py +34 -22
- pyopenapi_gen/visit/model/model_visitor.py +7 -5
- {pyopenapi_gen-0.11.0.dist-info → pyopenapi_gen-0.12.0.dist-info}/METADATA +3 -3
- {pyopenapi_gen-0.11.0.dist-info → pyopenapi_gen-0.12.0.dist-info}/RECORD +29 -29
- {pyopenapi_gen-0.11.0.dist-info → pyopenapi_gen-0.12.0.dist-info}/WHEEL +0 -0
- {pyopenapi_gen-0.11.0.dist-info → pyopenapi_gen-0.12.0.dist-info}/entry_points.txt +0 -0
- {pyopenapi_gen-0.11.0.dist-info → pyopenapi_gen-0.12.0.dist-info}/licenses/LICENSE +0 -0
@@ -148,8 +148,10 @@ class SpecLoader:
|
|
148
148
|
)
|
149
149
|
|
150
150
|
# Post-condition check
|
151
|
-
|
152
|
-
|
151
|
+
if ir_spec.schemas != schemas_dict:
|
152
|
+
raise RuntimeError("Schemas mismatch in IRSpec")
|
153
|
+
if ir_spec.operations != operations:
|
154
|
+
raise RuntimeError("Operations mismatch in IRSpec")
|
153
155
|
|
154
156
|
return ir_spec
|
155
157
|
|
@@ -39,11 +39,16 @@ def parse_operations(
|
|
39
39
|
- All operations have correct path, method, parameters, responses, etc.
|
40
40
|
- All referenced schemas are properly stored in context
|
41
41
|
"""
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
if not isinstance(paths, Mapping):
|
43
|
+
raise TypeError("paths must be a Mapping")
|
44
|
+
if not isinstance(raw_parameters, Mapping):
|
45
|
+
raise TypeError("raw_parameters must be a Mapping")
|
46
|
+
if not isinstance(raw_responses, Mapping):
|
47
|
+
raise TypeError("raw_responses must be a Mapping")
|
48
|
+
if not isinstance(raw_request_bodies, Mapping):
|
49
|
+
raise TypeError("raw_request_bodies must be a Mapping")
|
50
|
+
if not isinstance(context, ParsingContext):
|
51
|
+
raise TypeError("context must be a ParsingContext")
|
47
52
|
|
48
53
|
ops: List[IROperation] = []
|
49
54
|
|
@@ -150,6 +155,7 @@ def parse_operations(
|
|
150
155
|
ops.append(op)
|
151
156
|
|
152
157
|
# Post-condition check
|
153
|
-
|
158
|
+
if not all(isinstance(op, IROperation) for op in ops):
|
159
|
+
raise TypeError("All items must be IROperation objects")
|
154
160
|
|
155
161
|
return ops
|
@@ -24,8 +24,10 @@ def post_process_operation(op: IROperation, context: ParsingContext) -> None:
|
|
24
24
|
Postconditions:
|
25
25
|
- All request body and response schemas are properly named and registered
|
26
26
|
"""
|
27
|
-
|
28
|
-
|
27
|
+
if not isinstance(op, IROperation):
|
28
|
+
raise TypeError("op must be an IROperation")
|
29
|
+
if not isinstance(context, ParsingContext):
|
30
|
+
raise TypeError("context must be a ParsingContext")
|
29
31
|
|
30
32
|
# Handle request body schemas
|
31
33
|
if op.request_body:
|
@@ -33,10 +33,14 @@ def parse_request_body(
|
|
33
33
|
- Returns a properly populated IRRequestBody or None if invalid
|
34
34
|
- All content media types are properly mapped to schemas
|
35
35
|
"""
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
if not isinstance(rb_node, Mapping):
|
37
|
+
raise TypeError("rb_node must be a Mapping")
|
38
|
+
if not isinstance(raw_request_bodies, Mapping):
|
39
|
+
raise TypeError("raw_request_bodies must be a Mapping")
|
40
|
+
if not isinstance(context, ParsingContext):
|
41
|
+
raise TypeError("context must be a ParsingContext")
|
42
|
+
if not operation_id:
|
43
|
+
raise ValueError("operation_id must be provided")
|
40
44
|
|
41
45
|
# Handle $ref in request body
|
42
46
|
if (
|
@@ -80,6 +84,7 @@ def parse_request_body(
|
|
80
84
|
request_body = IRRequestBody(required=required_flag, content=content_map, description=desc)
|
81
85
|
|
82
86
|
# Post-condition check
|
83
|
-
|
87
|
+
if request_body.content != content_map:
|
88
|
+
raise RuntimeError("Request body content mismatch")
|
84
89
|
|
85
90
|
return request_body
|
@@ -27,8 +27,10 @@ def resolve_parameter_node_if_ref(param_node_data: Mapping[str, Any], context: P
|
|
27
27
|
- Returns the resolved parameter node or the original if not a ref
|
28
28
|
- If a reference, the parameter is looked up in components
|
29
29
|
"""
|
30
|
-
|
31
|
-
|
30
|
+
if not isinstance(param_node_data, Mapping):
|
31
|
+
raise TypeError("param_node_data must be a Mapping")
|
32
|
+
if not isinstance(context, ParsingContext):
|
33
|
+
raise TypeError("context must be a ParsingContext")
|
32
34
|
|
33
35
|
if "$ref" in param_node_data and isinstance(param_node_data.get("$ref"), str):
|
34
36
|
ref_path = param_node_data["$ref"]
|
@@ -62,9 +64,12 @@ def parse_parameter(
|
|
62
64
|
- Returns a properly populated IRParameter
|
63
65
|
- Complex parameter schemas are given appropriate names
|
64
66
|
"""
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
if not isinstance(node, Mapping):
|
68
|
+
raise TypeError("node must be a Mapping")
|
69
|
+
if "name" not in node:
|
70
|
+
raise ValueError("Parameter node must have a name")
|
71
|
+
if not isinstance(context, ParsingContext):
|
72
|
+
raise TypeError("context must be a ParsingContext")
|
68
73
|
|
69
74
|
sch = node.get("schema")
|
70
75
|
param_name = node["name"]
|
@@ -114,12 +119,38 @@ def parse_parameter(
|
|
114
119
|
and "enum" in sch["items"]
|
115
120
|
and "$ref" not in sch["items"]
|
116
121
|
):
|
117
|
-
# This is an array of string enums -
|
118
|
-
#
|
122
|
+
# This is an array of string enums - create a proper enum schema for the items
|
123
|
+
# Give it a name based on the parameter and operation
|
124
|
+
enum_name = None
|
125
|
+
if operation_id_for_promo and param_name:
|
126
|
+
# Create a name for this inline enum when we have operation context
|
127
|
+
enum_name = f"{operation_id_for_promo}Param{NameSanitizer.sanitize_class_name(param_name)}Item"
|
128
|
+
elif param_name:
|
129
|
+
# For component parameters without operation context, use just the parameter name
|
130
|
+
enum_name = f"{NameSanitizer.sanitize_class_name(param_name)}Item"
|
131
|
+
|
132
|
+
if enum_name:
|
133
|
+
items_schema = IRSchema(
|
134
|
+
name=enum_name,
|
135
|
+
type="string",
|
136
|
+
enum=sch["items"]["enum"],
|
137
|
+
generation_name=enum_name, # Mark it as promoted
|
138
|
+
)
|
139
|
+
logger.debug(
|
140
|
+
f"Created enum schema '{enum_name}' for array parameter '{param_name}' with values {sch['items']['enum'][:3]}..."
|
141
|
+
)
|
142
|
+
else:
|
143
|
+
# Fallback if we don't have enough info to create a good name
|
144
|
+
items_schema = IRSchema(name=None, type="string", enum=sch["items"]["enum"])
|
145
|
+
logger.warning(
|
146
|
+
f"Could not create proper enum name for parameter array items with values {sch['items']['enum'][:3]}... "
|
147
|
+
f"This will generate a warning during type resolution."
|
148
|
+
)
|
149
|
+
|
119
150
|
schema_ir = IRSchema(
|
120
151
|
name=None,
|
121
152
|
type="array",
|
122
|
-
items=
|
153
|
+
items=items_schema,
|
123
154
|
description=sch.get("description"),
|
124
155
|
)
|
125
156
|
else:
|
@@ -138,7 +169,9 @@ def parse_parameter(
|
|
138
169
|
)
|
139
170
|
|
140
171
|
# Post-condition check
|
141
|
-
|
142
|
-
|
172
|
+
if param.name != node["name"]:
|
173
|
+
raise RuntimeError("Parameter name mismatch")
|
174
|
+
if param.schema is None:
|
175
|
+
raise RuntimeError("Parameter schema must be created")
|
143
176
|
|
144
177
|
return param
|
@@ -34,10 +34,14 @@ def parse_response(
|
|
34
34
|
- All content media types are properly mapped to schemas
|
35
35
|
- Stream flags are correctly set based on media types
|
36
36
|
"""
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
if not isinstance(code, str):
|
38
|
+
raise TypeError("code must be a string")
|
39
|
+
if not isinstance(node, Mapping):
|
40
|
+
raise TypeError("node must be a Mapping")
|
41
|
+
if not isinstance(context, ParsingContext):
|
42
|
+
raise TypeError("context must be a ParsingContext")
|
43
|
+
if not operation_id_for_promo:
|
44
|
+
raise ValueError("operation_id_for_promo must be provided")
|
41
45
|
|
42
46
|
content: Dict[str, IRSchema] = {}
|
43
47
|
STREAM_FORMATS = {
|
@@ -97,8 +101,11 @@ def parse_response(
|
|
97
101
|
)
|
98
102
|
|
99
103
|
# Post-condition checks
|
100
|
-
|
101
|
-
|
102
|
-
|
104
|
+
if response.status_code != code:
|
105
|
+
raise RuntimeError("Response status code mismatch")
|
106
|
+
if response.content != content:
|
107
|
+
raise RuntimeError("Response content mismatch")
|
108
|
+
if response.stream != stream_flag:
|
109
|
+
raise RuntimeError("Response stream flag mismatch")
|
103
110
|
|
104
111
|
return response
|
@@ -28,8 +28,10 @@ def build_schemas(raw_schemas: Dict[str, Mapping[str, Any]], raw_components: Map
|
|
28
28
|
- A ParsingContext is returned with all schemas parsed
|
29
29
|
- All schemas in raw_schemas are populated in context.parsed_schemas
|
30
30
|
"""
|
31
|
-
|
32
|
-
|
31
|
+
if not isinstance(raw_schemas, dict):
|
32
|
+
raise TypeError("raw_schemas must be a dict")
|
33
|
+
if not isinstance(raw_components, Mapping):
|
34
|
+
raise TypeError("raw_components must be a Mapping")
|
33
35
|
|
34
36
|
context = ParsingContext(raw_spec_schemas=raw_schemas, raw_spec_components=raw_components)
|
35
37
|
|
@@ -39,7 +41,8 @@ def build_schemas(raw_schemas: Dict[str, Mapping[str, Any]], raw_components: Map
|
|
39
41
|
_parse_schema(n, nd, context, allow_self_reference=True)
|
40
42
|
|
41
43
|
# Post-condition check
|
42
|
-
|
44
|
+
if not all(n in context.parsed_schemas for n in raw_schemas):
|
45
|
+
raise RuntimeError("Not all schemas were parsed")
|
43
46
|
|
44
47
|
return context
|
45
48
|
|
@@ -55,8 +58,10 @@ def extract_inline_array_items(schemas: Dict[str, IRSchema]) -> Dict[str, IRSche
|
|
55
58
|
- All array item schemas have proper names
|
56
59
|
- No duplicate schema names are created
|
57
60
|
"""
|
58
|
-
|
59
|
-
|
61
|
+
if not isinstance(schemas, dict):
|
62
|
+
raise TypeError("schemas must be a dict")
|
63
|
+
if not all(isinstance(s, IRSchema) for s in schemas.values()):
|
64
|
+
raise TypeError("all values must be IRSchema objects")
|
60
65
|
|
61
66
|
# Store original schema count for post-condition validation
|
62
67
|
original_schema_count = len(schemas)
|
@@ -119,8 +124,10 @@ def extract_inline_array_items(schemas: Dict[str, IRSchema]) -> Dict[str, IRSche
|
|
119
124
|
schemas.update(new_item_schemas)
|
120
125
|
|
121
126
|
# Post-condition checks
|
122
|
-
|
123
|
-
|
127
|
+
if len(schemas) < original_schema_count:
|
128
|
+
raise RuntimeError("Schemas count should not decrease")
|
129
|
+
if not original_schemas.issubset(set(schemas.keys())):
|
130
|
+
raise RuntimeError("Original schemas should still be present")
|
124
131
|
|
125
132
|
return schemas
|
126
133
|
|
@@ -140,8 +147,10 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
|
|
140
147
|
- No duplicate schema names are created
|
141
148
|
- Top-level enum schemas have generation_name set
|
142
149
|
"""
|
143
|
-
|
144
|
-
|
150
|
+
if not isinstance(schemas, dict):
|
151
|
+
raise TypeError("schemas must be a dict")
|
152
|
+
if not all(isinstance(s, IRSchema) for s in schemas.values()):
|
153
|
+
raise TypeError("all values must be IRSchema objects")
|
145
154
|
|
146
155
|
# Store original schema count for post-condition validation
|
147
156
|
original_schema_count = len(schemas)
|
@@ -160,6 +169,9 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
|
|
160
169
|
# but we can set it here to avoid the warning)
|
161
170
|
if not hasattr(schema, "generation_name") or not schema.generation_name:
|
162
171
|
schema.generation_name = schema.name
|
172
|
+
logger.info(
|
173
|
+
f"Set generation_name for top-level enum schema: {schema_name} with values {schema.enum[:3]}..."
|
174
|
+
)
|
163
175
|
# Mark this as a properly processed enum by ensuring generation_name is set
|
164
176
|
# This serves as the marker that this enum was properly processed
|
165
177
|
logger.debug(f"Marked top-level enum schema: {schema_name}")
|
@@ -195,7 +207,9 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
|
|
195
207
|
schemas.update(new_enums)
|
196
208
|
|
197
209
|
# Post-condition checks
|
198
|
-
|
199
|
-
|
210
|
+
if len(schemas) < original_schema_count:
|
211
|
+
raise RuntimeError("Schemas count should not decrease")
|
212
|
+
if not original_schemas.issubset(set(schemas.keys())):
|
213
|
+
raise RuntimeError("Original schemas should still be present")
|
200
214
|
|
201
215
|
return schemas
|
@@ -169,7 +169,8 @@ class ParsingContext:
|
|
169
169
|
Postconditions:
|
170
170
|
- Returns True if the schema exists in parsed_schemas, False otherwise
|
171
171
|
"""
|
172
|
-
|
172
|
+
if not isinstance(schema_name, str):
|
173
|
+
raise TypeError("schema_name must be a string")
|
173
174
|
return schema_name in self.parsed_schemas
|
174
175
|
|
175
176
|
def get_parsed_schema(self, schema_name: str) -> Optional["IRSchema"]:
|
@@ -181,5 +182,6 @@ class ParsingContext:
|
|
181
182
|
Postconditions:
|
182
183
|
- Returns the IRSchema if it exists, None otherwise
|
183
184
|
"""
|
184
|
-
|
185
|
+
if not isinstance(schema_name, str):
|
186
|
+
raise TypeError("schema_name must be a string")
|
185
187
|
return self.parsed_schemas.get(schema_name)
|
@@ -43,10 +43,14 @@ def _process_all_of(
|
|
43
43
|
- parsed_all_of_components: List of IRSchema for each item in 'allOf' (empty if 'allOf' not present).
|
44
44
|
"""
|
45
45
|
# Pre-conditions
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
if not (isinstance(node, Mapping) and node):
|
47
|
+
raise TypeError("node must be a non-empty Mapping")
|
48
|
+
if not isinstance(context, ParsingContext):
|
49
|
+
raise TypeError("context must be a ParsingContext instance")
|
50
|
+
if not callable(_parse_schema_func):
|
51
|
+
raise TypeError("_parse_schema_func must be callable")
|
52
|
+
if not (isinstance(max_depth, int) and max_depth >= 0):
|
53
|
+
raise ValueError("max_depth must be a non-negative integer")
|
50
54
|
|
51
55
|
parsed_all_of_components: List[IRSchema] = []
|
52
56
|
merged_required: Set[str] = set(node.get("required", []))
|
@@ -38,11 +38,16 @@ def _parse_any_of_schemas(
|
|
38
38
|
- is_nullable: True if a null type was present.
|
39
39
|
- effective_schema_type: Potential schema_type if list becomes empty/None (currently always None).
|
40
40
|
"""
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
if not isinstance(any_of_nodes, list):
|
42
|
+
raise TypeError("any_of_nodes must be a list")
|
43
|
+
if not all(isinstance(n, Mapping) for n in any_of_nodes):
|
44
|
+
raise TypeError("all items in any_of_nodes must be Mappings")
|
45
|
+
if not isinstance(context, ParsingContext):
|
46
|
+
raise TypeError("context must be a ParsingContext instance")
|
47
|
+
if not max_depth >= 0:
|
48
|
+
raise ValueError("max_depth must be non-negative")
|
49
|
+
if not callable(parse_fn):
|
50
|
+
raise TypeError("parse_fn must be a callable")
|
46
51
|
|
47
52
|
parsed_schemas_list: List[IRSchema] = [] # Renamed to avoid confusion with module name
|
48
53
|
is_nullable_from_any_of = False
|
@@ -51,9 +51,12 @@ def _parse_array_items_schema(
|
|
51
51
|
"""
|
52
52
|
# Pre-conditions
|
53
53
|
# items_node_data is checked later, as it can be non-Mapping to return None
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
if not isinstance(context, ParsingContext):
|
55
|
+
raise TypeError("context must be a ParsingContext instance")
|
56
|
+
if not callable(parse_fn):
|
57
|
+
raise TypeError("parse_fn must be callable")
|
58
|
+
if not (isinstance(max_depth, int) and max_depth >= 0):
|
59
|
+
raise ValueError("max_depth must be a non-negative integer")
|
57
60
|
|
58
61
|
item_name_for_parse = f"{parent_schema_name}Item" if parent_schema_name else None
|
59
62
|
if (
|
@@ -35,11 +35,16 @@ def _parse_one_of_schemas(
|
|
35
35
|
- is_nullable: True if a null type was present.
|
36
36
|
- effective_schema_type: Potential schema_type if list becomes empty/None (currently always None).
|
37
37
|
"""
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
if not isinstance(one_of_nodes, list):
|
39
|
+
raise TypeError("one_of_nodes must be a list")
|
40
|
+
if not all(isinstance(n, Mapping) for n in one_of_nodes):
|
41
|
+
raise TypeError("all items in one_of_nodes must be Mappings")
|
42
|
+
if not isinstance(context, ParsingContext):
|
43
|
+
raise TypeError("context must be a ParsingContext instance")
|
44
|
+
if not max_depth >= 0:
|
45
|
+
raise ValueError("max_depth must be non-negative")
|
46
|
+
if not callable(parse_fn):
|
47
|
+
raise TypeError("parse_fn must be a callable")
|
43
48
|
|
44
49
|
parsed_schemas_list: List[IRSchema] = []
|
45
50
|
is_nullable_from_one_of = False
|
@@ -53,9 +53,12 @@ def _finalize_schema_object(
|
|
53
53
|
- Returns a finalized IRSchema instance.
|
54
54
|
- If schema has a name and isn't a placeholder, it's in context.parsed_schemas.
|
55
55
|
"""
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
if not isinstance(node, Mapping):
|
57
|
+
raise TypeError("node must be a Mapping")
|
58
|
+
if not isinstance(context, ParsingContext):
|
59
|
+
raise TypeError("context must be a ParsingContext instance")
|
60
|
+
if not callable(parse_fn):
|
61
|
+
raise TypeError("parse_fn must be callable")
|
59
62
|
# Remove logger type check to support mock loggers in tests
|
60
63
|
|
61
64
|
# If a placeholder for this schema name already exists due to a cycle detected deeper,
|
@@ -326,7 +326,8 @@ def _parse_schema(
|
|
326
326
|
Parse a schema node and return an IRSchema object.
|
327
327
|
"""
|
328
328
|
# Pre-conditions
|
329
|
-
|
329
|
+
if context is None:
|
330
|
+
raise ValueError("Context cannot be None for _parse_schema")
|
330
331
|
|
331
332
|
# Set allow_self_reference flag on unified context
|
332
333
|
context.unified_cycle_context.allow_self_reference = allow_self_reference
|
@@ -377,9 +378,10 @@ def _parse_schema(
|
|
377
378
|
if schema_node is None:
|
378
379
|
return IRSchema(name=NameSanitizer.sanitize_class_name(schema_name) if schema_name else None)
|
379
380
|
|
380
|
-
|
381
|
-
|
382
|
-
|
381
|
+
if not isinstance(schema_node, Mapping):
|
382
|
+
raise TypeError(
|
383
|
+
f"Schema node for '{schema_name or 'anonymous'}' must be a Mapping (e.g., dict), got {type(schema_node)}"
|
384
|
+
)
|
383
385
|
|
384
386
|
# If the current schema_node itself is a $ref, resolve it.
|
385
387
|
if "$ref" in schema_node:
|
@@ -197,7 +197,8 @@ class EndpointsEmitter:
|
|
197
197
|
self._deduplicate_operation_ids(ops_for_tag)
|
198
198
|
|
199
199
|
# EndpointVisitor must exist here due to check above
|
200
|
-
|
200
|
+
if self.visitor is None:
|
201
|
+
raise RuntimeError("EndpointVisitor not initialized")
|
201
202
|
methods = [self.visitor.visit(op, self.context) for op in ops_for_tag]
|
202
203
|
class_content = self.visitor.emit_endpoint_client_class(canonical_tag_name, methods, self.context)
|
203
204
|
|
@@ -42,12 +42,10 @@ class ModelsEmitter:
|
|
42
42
|
# )
|
43
43
|
|
44
44
|
# Assert that de-collided names have been set by the emit() method's preprocessing.
|
45
|
-
|
46
|
-
schema_ir.generation_name
|
47
|
-
|
48
|
-
|
49
|
-
schema_ir.final_module_stem is not None
|
50
|
-
), f"Schema '{schema_ir.name}' must have final_module_stem set before file generation."
|
45
|
+
if schema_ir.generation_name is None:
|
46
|
+
raise RuntimeError(f"Schema '{schema_ir.name}' must have generation_name set before file generation.")
|
47
|
+
if schema_ir.final_module_stem is None:
|
48
|
+
raise RuntimeError(f"Schema '{schema_ir.name}' must have final_module_stem set before file generation.")
|
51
49
|
|
52
50
|
file_path = models_dir / f"{schema_ir.final_module_stem}.py"
|
53
51
|
|
@@ -138,12 +136,10 @@ class ModelsEmitter:
|
|
138
136
|
|
139
137
|
for s_schema in sorted_schemas_for_init:
|
140
138
|
# These should have been set in the emit() preprocessing step.
|
141
|
-
|
142
|
-
s_schema.generation_name
|
143
|
-
|
144
|
-
|
145
|
-
s_schema.final_module_stem is not None
|
146
|
-
), f"Schema '{s_schema.name}' missing final_module_stem in __init__ generation."
|
139
|
+
if s_schema.generation_name is None:
|
140
|
+
raise RuntimeError(f"Schema '{s_schema.name}' missing generation_name in __init__ generation.")
|
141
|
+
if s_schema.final_module_stem is None:
|
142
|
+
raise RuntimeError(f"Schema '{s_schema.name}' missing final_module_stem in __init__ generation.")
|
147
143
|
|
148
144
|
if s_schema._from_unresolved_ref: # Check this flag if it's relevant
|
149
145
|
# logger.debug(
|
@@ -184,8 +180,10 @@ class ModelsEmitter:
|
|
184
180
|
- All models are properly formatted and type-annotated
|
185
181
|
- Returns a list of file paths generated
|
186
182
|
"""
|
187
|
-
|
188
|
-
|
183
|
+
if not isinstance(spec, IRSpec):
|
184
|
+
raise TypeError("spec must be an IRSpec")
|
185
|
+
if not output_root:
|
186
|
+
raise ValueError("output_root must be a non-empty string")
|
189
187
|
|
190
188
|
output_dir = Path(output_root.rstrip("/"))
|
191
189
|
models_dir = output_dir / "models"
|
@@ -75,15 +75,14 @@ class NamedTypeResolver:
|
|
75
75
|
if schema.name and schema.name in self.all_schemas:
|
76
76
|
# This schema is a REFERENCE to a globally defined schema (e.g., in components/schemas)
|
77
77
|
ref_schema = self.all_schemas[schema.name] # Get the actual definition
|
78
|
-
|
78
|
+
if ref_schema.name is None:
|
79
|
+
raise RuntimeError(f"Schema '{schema.name}' resolved to ref_schema with None name.")
|
79
80
|
|
80
81
|
# NEW: Use generation_name and final_module_stem from the referenced schema
|
81
|
-
|
82
|
-
ref_schema.
|
83
|
-
|
84
|
-
|
85
|
-
ref_schema.final_module_stem is not None
|
86
|
-
), f"Referenced schema '{ref_schema.name}' must have final_module_stem set."
|
82
|
+
if ref_schema.generation_name is None:
|
83
|
+
raise RuntimeError(f"Referenced schema '{ref_schema.name}' must have generation_name set.")
|
84
|
+
if ref_schema.final_module_stem is None:
|
85
|
+
raise RuntimeError(f"Referenced schema '{ref_schema.name}' must have final_module_stem set.")
|
87
86
|
|
88
87
|
class_name_for_ref = ref_schema.generation_name
|
89
88
|
module_name_for_ref = ref_schema.final_module_stem
|
@@ -113,12 +113,14 @@ class ObjectTypeResolver:
|
|
113
113
|
# If this named object is a component schema, ensure it's imported.
|
114
114
|
if schema.name and schema.name in self.all_schemas:
|
115
115
|
actual_schema_def = self.all_schemas[schema.name]
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
116
|
+
if actual_schema_def.generation_name is None:
|
117
|
+
raise RuntimeError(
|
118
|
+
f"Actual schema '{actual_schema_def.name}' for '{schema.name}' must have generation_name."
|
119
|
+
)
|
120
|
+
if actual_schema_def.final_module_stem is None:
|
121
|
+
raise RuntimeError(
|
122
|
+
f"Actual schema '{actual_schema_def.name}' for '{schema.name}' must have final_module_stem."
|
123
|
+
)
|
122
124
|
|
123
125
|
class_name_to_use = actual_schema_def.generation_name
|
124
126
|
module_stem_to_use = actual_schema_def.final_module_stem
|
@@ -162,14 +164,16 @@ class ObjectTypeResolver:
|
|
162
164
|
schema.name and schema.name in self.all_schemas
|
163
165
|
): # Named object, no properties, AND it's a known component
|
164
166
|
actual_schema_def = self.all_schemas[schema.name]
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
167
|
+
if actual_schema_def.generation_name is None:
|
168
|
+
raise RuntimeError(
|
169
|
+
f"Actual schema (no props) '{actual_schema_def.name}' "
|
170
|
+
f"for '{schema.name}' must have generation_name."
|
171
|
+
)
|
172
|
+
if actual_schema_def.final_module_stem is None:
|
173
|
+
raise RuntimeError(
|
174
|
+
f"Actual schema (no props) '{actual_schema_def.name}' "
|
175
|
+
f"for '{schema.name}' must have final_module_stem."
|
176
|
+
)
|
173
177
|
|
174
178
|
class_name_to_use = actual_schema_def.generation_name
|
175
179
|
module_stem_to_use = actual_schema_def.final_module_stem
|
@@ -181,8 +181,15 @@ class OpenAPISchemaResolver(SchemaTypeResolver):
|
|
181
181
|
# Return the enum type name
|
182
182
|
return ResolvedType(python_type=schema.generation_name, is_optional=not required)
|
183
183
|
else:
|
184
|
-
# This is an unprocessed inline enum - log warning
|
185
|
-
|
184
|
+
# This is an unprocessed inline enum - log warning with details
|
185
|
+
enum_values = schema.enum[:5] if len(schema.enum) > 5 else schema.enum
|
186
|
+
more = f" (and {len(schema.enum) - 5} more)" if len(schema.enum) > 5 else ""
|
187
|
+
context_info = f"name='{schema.name}'" if schema.name else "unnamed"
|
188
|
+
logger.warning(
|
189
|
+
f"Found inline enum in string schema that wasn't promoted: "
|
190
|
+
f"{context_info}, type={schema.type}, enum_values={enum_values}{more}. "
|
191
|
+
f"This will be treated as plain 'str' instead of a proper Enum type."
|
192
|
+
)
|
186
193
|
return ResolvedType(python_type="str", is_optional=not required)
|
187
194
|
|
188
195
|
# Handle string formats
|
@@ -388,7 +388,7 @@ class EndpointResponseHandlerGenerator:
|
|
388
388
|
# All code paths should be covered by the match statement above
|
389
389
|
writer.write_line("# All paths above should return or raise - this should never execute")
|
390
390
|
context.add_import("typing", "NoReturn")
|
391
|
-
writer.write_line("
|
391
|
+
writer.write_line("raise RuntimeError('Unexpected code path') # pragma: no cover")
|
392
392
|
writer.write_line("") # Add a blank line for readability
|
393
393
|
|
394
394
|
def _write_strategy_based_return(
|
@@ -24,7 +24,8 @@ class AliasGenerator:
|
|
24
24
|
all_schemas: Optional[Dict[str, IRSchema]],
|
25
25
|
):
|
26
26
|
# Pre-condition
|
27
|
-
|
27
|
+
if renderer is None:
|
28
|
+
raise ValueError("PythonConstructRenderer cannot be None")
|
28
29
|
self.renderer = renderer
|
29
30
|
self.all_schemas = all_schemas if all_schemas is not None else {}
|
30
31
|
self.type_service = UnifiedTypeService(self.all_schemas)
|
@@ -58,10 +59,14 @@ class AliasGenerator:
|
|
58
59
|
- ``TypeAlias`` is imported in the context if not already present.
|
59
60
|
"""
|
60
61
|
# Pre-conditions
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
if schema is None:
|
63
|
+
raise ValueError("Schema cannot be None for alias generation.")
|
64
|
+
if schema.name is None:
|
65
|
+
raise ValueError("Schema name must be present for alias generation.")
|
66
|
+
if not base_name:
|
67
|
+
raise ValueError("Base name cannot be empty for alias generation.")
|
68
|
+
if context is None:
|
69
|
+
raise ValueError("RenderContext cannot be None.")
|
65
70
|
|
66
71
|
alias_name = NameSanitizer.sanitize_class_name(base_name)
|
67
72
|
target_type = self.type_service.resolve_schema_type(schema, context, required=True, resolve_underlying=True)
|
@@ -77,13 +82,15 @@ class AliasGenerator:
|
|
77
82
|
)
|
78
83
|
|
79
84
|
# Post-condition
|
80
|
-
|
85
|
+
if not rendered_code.strip():
|
86
|
+
raise RuntimeError("Generated alias code cannot be empty.")
|
81
87
|
# PythonConstructRenderer is responsible for adding TypeAlias import
|
82
88
|
# We can check if it was added to context if 'TypeAlias' is in the rendered code
|
83
89
|
if "TypeAlias" in rendered_code:
|
84
|
-
|
90
|
+
if not (
|
85
91
|
"typing" in context.import_collector.imports
|
86
92
|
and "TypeAlias" in context.import_collector.imports["typing"]
|
87
|
-
)
|
93
|
+
):
|
94
|
+
raise RuntimeError("TypeAlias import was not added to context by renderer.")
|
88
95
|
|
89
96
|
return rendered_code
|
@@ -31,7 +31,8 @@ class DataclassGenerator:
|
|
31
31
|
Pre-conditions:
|
32
32
|
- ``renderer`` is not None.
|
33
33
|
"""
|
34
|
-
|
34
|
+
if renderer is None:
|
35
|
+
raise ValueError("PythonConstructRenderer cannot be None.")
|
35
36
|
self.renderer = renderer
|
36
37
|
self.all_schemas = all_schemas if all_schemas is not None else {}
|
37
38
|
self.type_service = UnifiedTypeService(self.all_schemas)
|
@@ -56,8 +57,10 @@ class DataclassGenerator:
|
|
56
57
|
- Returns a valid Python default value string
|
57
58
|
(e.g., "None", "field(default_factory=list)", "\"abc\"") or None.
|
58
59
|
"""
|
59
|
-
|
60
|
-
|
60
|
+
if ps is None:
|
61
|
+
raise ValueError("Property schema (ps) cannot be None.")
|
62
|
+
if context is None:
|
63
|
+
raise ValueError("RenderContext cannot be None.")
|
61
64
|
|
62
65
|
if ps.type == "array":
|
63
66
|
context.add_import("dataclasses", "field")
|
@@ -124,10 +127,14 @@ class DataclassGenerator:
|
|
124
127
|
- Returns a non-empty string containing valid Python code for a dataclass.
|
125
128
|
- ``@dataclass`` decorator is present, implying ``dataclasses.dataclass`` is imported.
|
126
129
|
"""
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
130
|
+
if schema is None:
|
131
|
+
raise ValueError("Schema cannot be None for dataclass generation.")
|
132
|
+
if schema.name is None:
|
133
|
+
raise ValueError("Schema name must be present for dataclass generation.")
|
134
|
+
if not base_name:
|
135
|
+
raise ValueError("Base name cannot be empty for dataclass generation.")
|
136
|
+
if context is None:
|
137
|
+
raise ValueError("RenderContext cannot be None.")
|
131
138
|
# Additional check for schema type might be too strict here, as ModelVisitor decides eligibility.
|
132
139
|
|
133
140
|
class_name = base_name
|
@@ -136,7 +143,8 @@ class DataclassGenerator:
|
|
136
143
|
|
137
144
|
if schema.type == "array" and schema.items:
|
138
145
|
field_name_for_array_content = "items"
|
139
|
-
|
146
|
+
if schema.items is None:
|
147
|
+
raise ValueError("Schema items must be present for array type dataclass field.")
|
140
148
|
|
141
149
|
list_item_py_type = self.type_service.resolve_schema_type(schema.items, context, required=True)
|
142
150
|
list_item_py_type = TypeFinalizer(context)._clean_type(list_item_py_type)
|
@@ -213,16 +221,18 @@ class DataclassGenerator:
|
|
213
221
|
field_mappings=field_mappings if field_mappings else None,
|
214
222
|
)
|
215
223
|
|
216
|
-
|
224
|
+
if not rendered_code.strip():
|
225
|
+
raise RuntimeError("Generated dataclass code cannot be empty.")
|
217
226
|
# PythonConstructRenderer adds the @dataclass decorator and import
|
218
|
-
|
219
|
-
|
227
|
+
if "@dataclass" not in rendered_code:
|
228
|
+
raise RuntimeError("Dataclass code missing @dataclass decorator.")
|
229
|
+
if not (
|
220
230
|
"dataclasses" in context.import_collector.imports
|
221
231
|
and "dataclass" in context.import_collector.imports["dataclasses"]
|
222
|
-
)
|
232
|
+
):
|
233
|
+
raise RuntimeError("dataclass import was not added to context by renderer.")
|
223
234
|
if "default_factory" in rendered_code: # Check for field import if factory is used
|
224
|
-
|
225
|
-
"dataclasses"
|
226
|
-
), "'field' import from dataclasses missing when default_factory is used."
|
235
|
+
if "field" not in context.import_collector.imports.get("dataclasses", set()):
|
236
|
+
raise RuntimeError("'field' import from dataclasses missing when default_factory is used.")
|
227
237
|
|
228
238
|
return rendered_code
|
@@ -19,7 +19,8 @@ class EnumGenerator:
|
|
19
19
|
|
20
20
|
def __init__(self, renderer: PythonConstructRenderer):
|
21
21
|
# Pre-condition
|
22
|
-
|
22
|
+
if renderer is None:
|
23
|
+
raise ValueError("PythonConstructRenderer cannot be None")
|
23
24
|
self.renderer = renderer
|
24
25
|
|
25
26
|
def _generate_member_name_for_string_enum(self, value: str) -> str:
|
@@ -32,7 +33,8 @@ class EnumGenerator:
|
|
32
33
|
Post-conditions:
|
33
34
|
- Returns a non-empty string that is a valid Python identifier, typically uppercase.
|
34
35
|
"""
|
35
|
-
|
36
|
+
if not isinstance(value, str):
|
37
|
+
raise TypeError("Input value must be a string.")
|
36
38
|
base_member_name = str(value).upper().replace("-", "_").replace(" ", "_")
|
37
39
|
sanitized_member_name = re.sub(r"[^A-Z0-9_]", "", base_member_name)
|
38
40
|
|
@@ -58,10 +60,11 @@ class EnumGenerator:
|
|
58
60
|
if not re.match(r"^[A-Z_]", sanitized_member_name.upper()):
|
59
61
|
sanitized_member_name = f"MEMBER_{sanitized_member_name}"
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
if not (sanitized_member_name and re.match(r"^[A-Z_][A-Z0-9_]*$", sanitized_member_name.upper())):
|
64
|
+
raise ValueError(
|
65
|
+
f"Generated string enum member name '{sanitized_member_name}' "
|
66
|
+
f"is not a valid Python identifier from value '{value}'."
|
67
|
+
)
|
65
68
|
return sanitized_member_name
|
66
69
|
|
67
70
|
def _generate_member_name_for_integer_enum(self, value: str | int, int_value_for_fallback: int) -> str:
|
@@ -75,8 +78,10 @@ class EnumGenerator:
|
|
75
78
|
Post-conditions:
|
76
79
|
- Returns a non-empty string that is a valid Python identifier, typically uppercase.
|
77
80
|
"""
|
78
|
-
|
79
|
-
|
81
|
+
if not isinstance(value, (str, int)):
|
82
|
+
raise TypeError("Input value for integer enum naming must be str or int.")
|
83
|
+
if not isinstance(int_value_for_fallback, int):
|
84
|
+
raise TypeError("Fallback integer value must be an int.")
|
80
85
|
|
81
86
|
name_basis = str(value) # Use string representation as basis for name
|
82
87
|
base_member_name = name_basis.upper().replace("-", "_").replace(" ", "_").replace(".", "_DOT_")
|
@@ -105,10 +110,11 @@ class EnumGenerator:
|
|
105
110
|
if not sanitized_member_name: # Should be impossible
|
106
111
|
sanitized_member_name = f"ENUM_MEMBER_UNKNOWN_{abs(int_value_for_fallback)}"
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
113
|
+
if not (sanitized_member_name and re.match(r"^[A-Z_][A-Z0-9_]*$", sanitized_member_name.upper())):
|
114
|
+
raise ValueError(
|
115
|
+
f"Generated integer enum member name '{sanitized_member_name}' "
|
116
|
+
f"is not a valid Python identifier from value '{value}'."
|
117
|
+
)
|
112
118
|
return sanitized_member_name
|
113
119
|
|
114
120
|
def generate(
|
@@ -138,12 +144,18 @@ class EnumGenerator:
|
|
138
144
|
- Returns a non-empty string containing valid Python code for an enum.
|
139
145
|
- ``Enum`` from the ``enum`` module is imported in the context.
|
140
146
|
"""
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
+
if schema is None:
|
148
|
+
raise ValueError("Schema cannot be None for enum generation.")
|
149
|
+
if schema.name is None:
|
150
|
+
raise ValueError("Schema name must be present for enum generation.")
|
151
|
+
if not base_name:
|
152
|
+
raise ValueError("Base name cannot be empty for enum generation.")
|
153
|
+
if context is None:
|
154
|
+
raise ValueError("RenderContext cannot be None.")
|
155
|
+
if not schema.enum:
|
156
|
+
raise ValueError("Schema must have enum values for enum generation.")
|
157
|
+
if schema.type not in ("string", "integer"):
|
158
|
+
raise ValueError("Enum schema type must be 'string' or 'integer'.")
|
147
159
|
|
148
160
|
enum_class_name = base_name # PythonConstructRenderer will sanitize this class name
|
149
161
|
base_type = "str" if schema.type == "string" else "int"
|
@@ -192,9 +204,9 @@ class EnumGenerator:
|
|
192
204
|
context=context,
|
193
205
|
)
|
194
206
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
207
|
+
if not rendered_code.strip():
|
208
|
+
raise RuntimeError("Generated enum code cannot be empty.")
|
209
|
+
if not ("enum" in context.import_collector.imports and "Enum" in context.import_collector.imports["enum"]):
|
210
|
+
raise RuntimeError("Enum import was not added to context by renderer.")
|
199
211
|
|
200
212
|
return rendered_code
|
@@ -114,7 +114,7 @@ class ModelVisitor(Visitor[IRSchema, str]):
|
|
114
114
|
logger.error(
|
115
115
|
f"ModelVisitor: Schema has no usable name (name or generation_name) for model generation: {schema}"
|
116
116
|
)
|
117
|
-
|
117
|
+
raise RuntimeError("Schema must have a name or generation_name for model code generation at this point.")
|
118
118
|
# return "" # Should not reach here if assertions are active
|
119
119
|
|
120
120
|
# --- Import Registration ---
|
@@ -144,13 +144,15 @@ class ModelVisitor(Visitor[IRSchema, str]):
|
|
144
144
|
# "alias, enum, or dataclass. No standalone model generated by ModelVisitor."
|
145
145
|
# )
|
146
146
|
# Post-condition: returns empty string if no specific generator called
|
147
|
-
|
147
|
+
if rendered_code:
|
148
|
+
raise RuntimeError("Rendered code should be empty if no generator was matched.")
|
148
149
|
return ""
|
149
150
|
|
150
151
|
# Post-condition: ensure some code was generated if a generator was called
|
151
|
-
|
152
|
-
|
153
|
-
|
152
|
+
if not rendered_code.strip() and (is_type_alias or is_enum or is_dataclass):
|
153
|
+
raise RuntimeError(
|
154
|
+
f"Code generation resulted in an empty string for schema '{schema.name}' which was matched as a model type."
|
155
|
+
)
|
154
156
|
|
155
157
|
return self.formatter.format(rendered_code)
|
156
158
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyopenapi-gen
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.12.0
|
4
4
|
Summary: Modern, async-first Python client generator for OpenAPI specifications with advanced cycle detection and unified type resolution
|
5
5
|
Project-URL: Homepage, https://github.com/your-org/pyopenapi-gen
|
6
6
|
Project-URL: Documentation, https://github.com/your-org/pyopenapi-gen/blob/main/README.md
|
@@ -32,13 +32,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
32
32
|
Classifier: Topic :: System :: Networking
|
33
33
|
Classifier: Typing :: Typed
|
34
34
|
Requires-Python: <4.0.0,>=3.12
|
35
|
-
Requires-Dist: click
|
35
|
+
Requires-Dist: click>=8.0.0
|
36
36
|
Requires-Dist: dataclass-wizard>=0.22.0
|
37
37
|
Requires-Dist: httpx>=0.24.0
|
38
38
|
Requires-Dist: openapi-core>=0.19
|
39
39
|
Requires-Dist: openapi-spec-validator>=0.7
|
40
40
|
Requires-Dist: pyyaml>=6.0
|
41
|
-
Requires-Dist: typer
|
41
|
+
Requires-Dist: typer>=0.14.0
|
42
42
|
Provides-Extra: dev
|
43
43
|
Requires-Dist: bandit[toml]>=1.7.0; extra == 'dev'
|
44
44
|
Requires-Dist: black>=23.0; extra == 'dev'
|
@@ -22,22 +22,22 @@ pyopenapi_gen/core/warning_collector.py,sha256=DYl9D7eZYs04mDU84KeonS-5-d0aM7hNq
|
|
22
22
|
pyopenapi_gen/core/auth/base.py,sha256=E2KUerA_mYv9D7xulUm-lenIxqZHqanjA4oRKpof2ZE,792
|
23
23
|
pyopenapi_gen/core/auth/plugins.py,sha256=bDWx4MTRFsCKp1i__BsQtZEvQPGU-NKI137-zoxmrgs,3465
|
24
24
|
pyopenapi_gen/core/loader/__init__.py,sha256=bt-MQ35fbq-f1YnCcopPg53TuXCI9_7wcMzQZoWVpjU,391
|
25
|
-
pyopenapi_gen/core/loader/loader.py,sha256=
|
25
|
+
pyopenapi_gen/core/loader/loader.py,sha256=fjRw6ZrG6hRS2RBWJY5IOv9e1ULc6tnxVGagqXhMmpk,6374
|
26
26
|
pyopenapi_gen/core/loader/operations/__init__.py,sha256=7se21D-BOy7Qw6C9auJ9v6D3NCuRiDpRlhqxGq11fJs,366
|
27
|
-
pyopenapi_gen/core/loader/operations/parser.py,sha256=
|
28
|
-
pyopenapi_gen/core/loader/operations/post_processor.py,sha256=
|
29
|
-
pyopenapi_gen/core/loader/operations/request_body.py,sha256=
|
27
|
+
pyopenapi_gen/core/loader/operations/parser.py,sha256=QHg3o8TRaReYofEEcBwuDuNxGDxZjkWXRDqOxKFiRtk,7046
|
28
|
+
pyopenapi_gen/core/loader/operations/post_processor.py,sha256=Rzb3GSiLyJk-0hTBZ6s6iWAj4KqE4Rfo3w-q2wm_R7w,2487
|
29
|
+
pyopenapi_gen/core/loader/operations/request_body.py,sha256=r-jscZEfOmqFA-E4i1Uj3S66rNEf1gDTlBiGUiA0P9k,3224
|
30
30
|
pyopenapi_gen/core/loader/parameters/__init__.py,sha256=p13oSibCRC5RCfsP6w7yD9MYs5TXcdI4WwPv7oGUYKk,284
|
31
|
-
pyopenapi_gen/core/loader/parameters/parser.py,sha256=
|
31
|
+
pyopenapi_gen/core/loader/parameters/parser.py,sha256=rOZWbykHQy7MH1gTHI61460mFG1nPKdU9A4hOsYAiIc,7505
|
32
32
|
pyopenapi_gen/core/loader/responses/__init__.py,sha256=6APWoH3IdNkgVmI0KsgZoZ6knDaG-S-pnUCa6gkzT8E,216
|
33
|
-
pyopenapi_gen/core/loader/responses/parser.py,sha256=
|
33
|
+
pyopenapi_gen/core/loader/responses/parser.py,sha256=F9otAn6ncQiY3C25Fq4mEJ5UFNgN83eujcX20Vd_nPU,4069
|
34
34
|
pyopenapi_gen/core/loader/schemas/__init__.py,sha256=rlhujYfw_IzWgzhVhYMJ3eIhE6C5Vi1Ylba-BHEVqOg,296
|
35
|
-
pyopenapi_gen/core/loader/schemas/extractor.py,sha256=
|
35
|
+
pyopenapi_gen/core/loader/schemas/extractor.py,sha256=n2jF_g8ZjA4GQiSKEWGAt35Hw7Ek93cnxFaS4Ld21Sc,9889
|
36
36
|
pyopenapi_gen/core/parsing/__init__.py,sha256=RJsIR6cHaNoI4tBcpMlAa0JsY64vsHb9sPxPg6rd8FQ,486
|
37
|
-
pyopenapi_gen/core/parsing/context.py,sha256=
|
37
|
+
pyopenapi_gen/core/parsing/context.py,sha256=8cM8mPItvDvJr8ZiukvdHBumlQl9hK1gUZL4BDpHaBk,8005
|
38
38
|
pyopenapi_gen/core/parsing/cycle_helpers.py,sha256=nG5ysNavL_6lpnHWFUZR9qraBxqOzuNfI6NgSEa8a5M,5939
|
39
|
-
pyopenapi_gen/core/parsing/schema_finalizer.py,sha256=
|
40
|
-
pyopenapi_gen/core/parsing/schema_parser.py,sha256=
|
39
|
+
pyopenapi_gen/core/parsing/schema_finalizer.py,sha256=qRTHUoVBQTgGmdfLuBuWxtWdj_SG71STGC3rn-tJvnA,6914
|
40
|
+
pyopenapi_gen/core/parsing/schema_parser.py,sha256=W0kCV0ucq6Wybr7x1wZl-nMSRXVWAqI4T57ASSabZsM,30557
|
41
41
|
pyopenapi_gen/core/parsing/unified_cycle_detection.py,sha256=3nplaCVh2dFwBPbmDc2kiU0SzTPXXktdQ5Rc0Q9Uu9s,10873
|
42
42
|
pyopenapi_gen/core/parsing/common/__init__.py,sha256=U3sHMO-l6S3Cm04CVOYmBCpqLEZvCylUI7yQfcTwxYU,27
|
43
43
|
pyopenapi_gen/core/parsing/common/type_parser.py,sha256=cK7xtxhoD43K2WjLP9TGip3As3akYeYW7L2XztXCecg,2562
|
@@ -52,10 +52,10 @@ pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py,sha256=B
|
|
52
52
|
pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py,sha256=MTaxiyqoPEaNFqF7tQHii3IH41M05wPKt6lBQi1SCDU,1625
|
53
53
|
pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py,sha256=ukKU64ozHINPiVlHE9YBDz1Nq2i6Xh64jhoWSEqbK5c,1774
|
54
54
|
pyopenapi_gen/core/parsing/keywords/__init__.py,sha256=enTLacWXGXLIOjSJ3j7KNUDzU27Kq3Ww79sFElz02cM,27
|
55
|
-
pyopenapi_gen/core/parsing/keywords/all_of_parser.py,sha256=
|
56
|
-
pyopenapi_gen/core/parsing/keywords/any_of_parser.py,sha256=
|
57
|
-
pyopenapi_gen/core/parsing/keywords/array_items_parser.py,sha256=
|
58
|
-
pyopenapi_gen/core/parsing/keywords/one_of_parser.py,sha256=
|
55
|
+
pyopenapi_gen/core/parsing/keywords/all_of_parser.py,sha256=ZH8rkxdDjldd226UGw5tv853ONXKaoqvWj5FfbgSoRY,3699
|
56
|
+
pyopenapi_gen/core/parsing/keywords/any_of_parser.py,sha256=HQ4iBBPgox48KIPPhM6VVZTXHIUVqpMoHkCbqcebXOw,2982
|
57
|
+
pyopenapi_gen/core/parsing/keywords/array_items_parser.py,sha256=nk9AFbx5ANLdDeGsxSTeYHhd8MqqrQwDRMzJ8FJmJxY,2723
|
58
|
+
pyopenapi_gen/core/parsing/keywords/one_of_parser.py,sha256=TUiT1dH1r8GEPExzlR3zdgnYpGxilH8yvnSWOvQmKmY,2727
|
59
59
|
pyopenapi_gen/core/parsing/keywords/properties_parser.py,sha256=bm248ApsNskFPQF4fXq7mT5oJf6FF9yAcdVLmK6el3E,4426
|
60
60
|
pyopenapi_gen/core/parsing/transformers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
61
61
|
pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py,sha256=hXuht-t0Syi5vPO8qn9GcjEpyDcZnfFVHVIlwfMa8L0,13461
|
@@ -70,9 +70,9 @@ pyopenapi_gen/emitters/CLAUDE.md,sha256=iZYEZq1a1h033rxuh97cMpsKUElv72ysvTm3-QQU
|
|
70
70
|
pyopenapi_gen/emitters/client_emitter.py,sha256=kmMVnG-wAOJm7TUm0xOQ5YnSJfYxz1SwtpiyoUCbcCA,1939
|
71
71
|
pyopenapi_gen/emitters/core_emitter.py,sha256=RcBsAYQ3ZKcWwtkzQmyHkL7VtDQjbIObFLXD9M_GdpI,8020
|
72
72
|
pyopenapi_gen/emitters/docs_emitter.py,sha256=aouKqhRdtVvYfGVsye_uqM80nONRy0SqN06cr1l3OgA,1137
|
73
|
-
pyopenapi_gen/emitters/endpoints_emitter.py,sha256=
|
73
|
+
pyopenapi_gen/emitters/endpoints_emitter.py,sha256=tzSLUzlZle2Lih_aZc4cJ-Y1ItjN5H_rABEWcDwECXA,9586
|
74
74
|
pyopenapi_gen/emitters/exceptions_emitter.py,sha256=qPTIPXDyqSUtpmBIp-V4ap1uMHUPmYziCSN62t7qcAE,1918
|
75
|
-
pyopenapi_gen/emitters/models_emitter.py,sha256=
|
75
|
+
pyopenapi_gen/emitters/models_emitter.py,sha256=Gd0z2Xoze1XkVnajkOptW90ti7197wQ15I7vIITnULM,22243
|
76
76
|
pyopenapi_gen/generator/CLAUDE.md,sha256=BS9KkmLvk2WD-Io-_apoWjGNeMU4q4LKy4UOxYF9WxM,10870
|
77
77
|
pyopenapi_gen/generator/client_generator.py,sha256=MULKJY9SdRuYjt_R4XCXh3vJSW-92rsxOu-MVpIklho,29333
|
78
78
|
pyopenapi_gen/helpers/CLAUDE.md,sha256=GyIJ0grp4SkD3plAUzyycW4nTUZf9ewtvvsdAGkmIZw,10609
|
@@ -85,8 +85,8 @@ pyopenapi_gen/helpers/type_resolution/__init__.py,sha256=TbaQZp7jvBiYgmuzuG8Wp56
|
|
85
85
|
pyopenapi_gen/helpers/type_resolution/array_resolver.py,sha256=dFppBtA6CxmiWAMR6rwGnQPv4AibL3nxtzw1LbeXVn4,2023
|
86
86
|
pyopenapi_gen/helpers/type_resolution/composition_resolver.py,sha256=wq6CRGxGgOKK470ln5Tpk9SzHtMuwB22TXHsRLtUFyw,3015
|
87
87
|
pyopenapi_gen/helpers/type_resolution/finalizer.py,sha256=_BcOBmOvadhBTUAvIc0Ak8FNxFw1uYL4rkKWtU68_m4,4332
|
88
|
-
pyopenapi_gen/helpers/type_resolution/named_resolver.py,sha256=
|
89
|
-
pyopenapi_gen/helpers/type_resolution/object_resolver.py,sha256=
|
88
|
+
pyopenapi_gen/helpers/type_resolution/named_resolver.py,sha256=hXu6Gao92EVV0jyHRqf9cIhoAFn3suuEw5ye9nRTFks,9423
|
89
|
+
pyopenapi_gen/helpers/type_resolution/object_resolver.py,sha256=Jm4m5QgzWU-4joPqfUSC0Y-za3Yx0ROdJjyv5J8VNk0,12557
|
90
90
|
pyopenapi_gen/helpers/type_resolution/primitive_resolver.py,sha256=qTshZaye5ohibVfcJYCzh4v3CAshguMGWPt2FgvQeNM,1960
|
91
91
|
pyopenapi_gen/helpers/type_resolution/resolver.py,sha256=qQY6wAitBluA-tofiyJ67Gxx8ol1W528zDWd9izYN5s,1982
|
92
92
|
pyopenapi_gen/types/CLAUDE.md,sha256=xRYlHdLhw3QGIfIlWqPt9pewRs736H1YCzwmslKtzZc,4255
|
@@ -97,7 +97,7 @@ pyopenapi_gen/types/contracts/types.py,sha256=-Qvbx3N_14AaN-1BeyocrvsjiwXPn_eWQh
|
|
97
97
|
pyopenapi_gen/types/resolvers/__init__.py,sha256=_5kA49RvyOTyXgt0GbbOfHJcdQw2zHxvU9af8GGyNWc,295
|
98
98
|
pyopenapi_gen/types/resolvers/reference_resolver.py,sha256=qnaZeLmtyh4_NBMcKib58s6o5ycUJaattYt8F38_qIo,2053
|
99
99
|
pyopenapi_gen/types/resolvers/response_resolver.py,sha256=Kb1a2803lyoukoZy06ztPBlUw-A1lHiZ6NlJmsixxA8,6500
|
100
|
-
pyopenapi_gen/types/resolvers/schema_resolver.py,sha256=
|
100
|
+
pyopenapi_gen/types/resolvers/schema_resolver.py,sha256=PsSF-DE8-GDXmvbAJz-tHlTCR-1UATGhT4dHf6kUDQQ,17988
|
101
101
|
pyopenapi_gen/types/services/__init__.py,sha256=inSUKmY_Vnuym6tC-AhvjCTj16GbkfxCGLESRr_uQPE,123
|
102
102
|
pyopenapi_gen/types/services/type_service.py,sha256=-LQj7oSx1mxb10Zi6DpawS8uyoUrUbnYhmUA0GuKZTc,4402
|
103
103
|
pyopenapi_gen/types/strategies/__init__.py,sha256=bju8_KEPNIow1-woMO-zJCgK_E0M6JnFq0NFsK1R4Ss,171
|
@@ -113,19 +113,19 @@ pyopenapi_gen/visit/endpoint/generators/__init__.py,sha256=-X-GYnJZ9twiEBr_U0obW
|
|
113
113
|
pyopenapi_gen/visit/endpoint/generators/docstring_generator.py,sha256=U02qvuYtFElQNEtOHuTNXFl2NxUriIiuZMkmUsapOg4,5913
|
114
114
|
pyopenapi_gen/visit/endpoint/generators/endpoint_method_generator.py,sha256=wUJ4_gaA1gRrFCHYFCObBIankxGQu0MNqiOSoZOZmoA,4352
|
115
115
|
pyopenapi_gen/visit/endpoint/generators/request_generator.py,sha256=OnkrkRk39_BrK9ZDvyWqJYLz1mocD2zY7j70yIpS0J4,5374
|
116
|
-
pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py,sha256=
|
116
|
+
pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py,sha256=xbh4GQcA-fA98mOZxVSylLbZADse-7RLcLIbjnVAmlE,22834
|
117
117
|
pyopenapi_gen/visit/endpoint/generators/signature_generator.py,sha256=CYtfsPMlTZN95g2WxrdnTloGx2RmqeNQRiyP9fOkUEQ,3892
|
118
118
|
pyopenapi_gen/visit/endpoint/generators/url_args_generator.py,sha256=EsmNuVSkGfUqrmV7-1GiLPzdN86V5UqLfs1SVY0jsf0,9590
|
119
119
|
pyopenapi_gen/visit/endpoint/processors/__init__.py,sha256=_6RqpOdDuDheArqDBi3ykhsaetACny88WUuuAJvr_ME,29
|
120
120
|
pyopenapi_gen/visit/endpoint/processors/import_analyzer.py,sha256=tNmhgWwt-CjLE774TC8sPVH1-yaTKKm6JmfgBT2-iRk,3386
|
121
121
|
pyopenapi_gen/visit/endpoint/processors/parameter_processor.py,sha256=BygP8yzSTrxfvNpEQIHERjd2NpEXDiwpCtmMseJKRq0,7700
|
122
122
|
pyopenapi_gen/visit/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
123
|
-
pyopenapi_gen/visit/model/alias_generator.py,sha256=
|
124
|
-
pyopenapi_gen/visit/model/dataclass_generator.py,sha256=
|
125
|
-
pyopenapi_gen/visit/model/enum_generator.py,sha256=
|
126
|
-
pyopenapi_gen/visit/model/model_visitor.py,sha256=
|
127
|
-
pyopenapi_gen-0.
|
128
|
-
pyopenapi_gen-0.
|
129
|
-
pyopenapi_gen-0.
|
130
|
-
pyopenapi_gen-0.
|
131
|
-
pyopenapi_gen-0.
|
123
|
+
pyopenapi_gen/visit/model/alias_generator.py,sha256=TGL3AMq_PkBWFWeeXbNnA8hgO9hvp0flwBA00Gr6S3o,3744
|
124
|
+
pyopenapi_gen/visit/model/dataclass_generator.py,sha256=nyTvBph6rtbJlCwTiDW_Y2UJmLLiA6D2QJUpA2xE0m8,10289
|
125
|
+
pyopenapi_gen/visit/model/enum_generator.py,sha256=AXqKUFuWUUjUF_6_HqBKY8vB5GYu35Pb2C2WPFrOw1k,10061
|
126
|
+
pyopenapi_gen/visit/model/model_visitor.py,sha256=4kAQSWsI4XumVYB3aAE7Ts_31hGfDlbytRalxyMFV3g,9510
|
127
|
+
pyopenapi_gen-0.12.0.dist-info/METADATA,sha256=do9Qt8fZ_bOvewCqxXYsCYbNE0qs7BtvC5Xe-AhBnBQ,14025
|
128
|
+
pyopenapi_gen-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
129
|
+
pyopenapi_gen-0.12.0.dist-info/entry_points.txt,sha256=gxSlNiwom50T3OEZnlocA6qRjGdV0bn6hN_Xr-Ub5wA,56
|
130
|
+
pyopenapi_gen-0.12.0.dist-info/licenses/LICENSE,sha256=UFAyTWKa4w10-QerlJaHJeep7G2gcwpf-JmvI2dS2Gc,1088
|
131
|
+
pyopenapi_gen-0.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|