pyopenapi-gen 0.10.2__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.
Files changed (33) hide show
  1. pyopenapi_gen/__init__.py +1 -1
  2. pyopenapi_gen/core/exceptions.py +1 -6
  3. pyopenapi_gen/core/loader/loader.py +19 -3
  4. pyopenapi_gen/core/loader/operations/parser.py +12 -6
  5. pyopenapi_gen/core/loader/operations/post_processor.py +4 -2
  6. pyopenapi_gen/core/loader/operations/request_body.py +10 -5
  7. pyopenapi_gen/core/loader/parameters/parser.py +66 -10
  8. pyopenapi_gen/core/loader/responses/parser.py +14 -7
  9. pyopenapi_gen/core/loader/schemas/extractor.py +42 -11
  10. pyopenapi_gen/core/parsing/context.py +4 -2
  11. pyopenapi_gen/core/parsing/keywords/all_of_parser.py +8 -4
  12. pyopenapi_gen/core/parsing/keywords/any_of_parser.py +10 -5
  13. pyopenapi_gen/core/parsing/keywords/array_items_parser.py +6 -3
  14. pyopenapi_gen/core/parsing/keywords/one_of_parser.py +10 -5
  15. pyopenapi_gen/core/parsing/schema_finalizer.py +6 -3
  16. pyopenapi_gen/core/parsing/schema_parser.py +6 -4
  17. pyopenapi_gen/emitters/endpoints_emitter.py +2 -1
  18. pyopenapi_gen/emitters/models_emitter.py +12 -14
  19. pyopenapi_gen/generator/client_generator.py +20 -5
  20. pyopenapi_gen/helpers/type_resolution/named_resolver.py +6 -7
  21. pyopenapi_gen/helpers/type_resolution/object_resolver.py +18 -14
  22. pyopenapi_gen/types/resolvers/schema_resolver.py +18 -3
  23. pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +1 -1
  24. pyopenapi_gen/visit/endpoint/generators/signature_generator.py +3 -11
  25. pyopenapi_gen/visit/model/alias_generator.py +15 -8
  26. pyopenapi_gen/visit/model/dataclass_generator.py +25 -15
  27. pyopenapi_gen/visit/model/enum_generator.py +34 -22
  28. pyopenapi_gen/visit/model/model_visitor.py +7 -5
  29. {pyopenapi_gen-0.10.2.dist-info → pyopenapi_gen-0.12.0.dist-info}/METADATA +3 -3
  30. {pyopenapi_gen-0.10.2.dist-info → pyopenapi_gen-0.12.0.dist-info}/RECORD +33 -33
  31. {pyopenapi_gen-0.10.2.dist-info → pyopenapi_gen-0.12.0.dist-info}/WHEEL +0 -0
  32. {pyopenapi_gen-0.10.2.dist-info → pyopenapi_gen-0.12.0.dist-info}/entry_points.txt +0 -0
  33. {pyopenapi_gen-0.10.2.dist-info → pyopenapi_gen-0.12.0.dist-info}/licenses/LICENSE +0 -0
pyopenapi_gen/__init__.py CHANGED
@@ -43,7 +43,7 @@ __all__ = [
43
43
  ]
44
44
 
45
45
  # Semantic version of the generator core – automatically managed by semantic-release.
46
- __version__: str = "0.9.0"
46
+ __version__: str = "0.10.2"
47
47
 
48
48
 
49
49
  # ---------------------------------------------------------------------------
@@ -1,12 +1,7 @@
1
- from typing import Optional
2
-
3
- import httpx
4
-
5
-
6
1
  class HTTPError(Exception):
7
2
  """Base HTTP error with status code and message."""
8
3
 
9
- def __init__(self, status_code: int, message: str, response: Optional[httpx.Response] = None) -> None:
4
+ def __init__(self, status_code: int, message: str, response: object | None = None) -> None:
10
5
  super().__init__(f"{status_code}: {message}")
11
6
  self.status_code = status_code
12
7
  self.message = message
@@ -87,8 +87,22 @@ class SpecLoader:
87
87
  validate_spec(cast(Mapping[Hashable, Any], self.spec))
88
88
  except Exception as e:
89
89
  warning_msg = f"OpenAPI spec validation error: {e}"
90
+ # Always collect the message
90
91
  warnings_list.append(warning_msg)
91
- warnings.warn(warning_msg, UserWarning)
92
+
93
+ # Heuristic: if this error originates from jsonschema or
94
+ # openapi_spec_validator, prefer logging over global warnings
95
+ # to avoid noisy test output while still surfacing the issue.
96
+ origin_module = getattr(e.__class__, "__module__", "")
97
+ if (
98
+ isinstance(e, RecursionError)
99
+ or origin_module.startswith("jsonschema")
100
+ or origin_module.startswith("openapi_spec_validator")
101
+ ):
102
+ logger.warning(warning_msg)
103
+ else:
104
+ # Preserve explicit warning behavior for unexpected failures
105
+ warnings.warn(warning_msg, UserWarning)
92
106
 
93
107
  return warnings_list
94
108
 
@@ -134,8 +148,10 @@ class SpecLoader:
134
148
  )
135
149
 
136
150
  # Post-condition check
137
- assert ir_spec.schemas == schemas_dict, "Schemas mismatch in IRSpec"
138
- assert ir_spec.operations == operations, "Operations mismatch in IRSpec"
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")
139
155
 
140
156
  return ir_spec
141
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
- assert isinstance(paths, Mapping), "paths must be a Mapping"
43
- assert isinstance(raw_parameters, Mapping), "raw_parameters must be a Mapping"
44
- assert isinstance(raw_responses, Mapping), "raw_responses must be a Mapping"
45
- assert isinstance(raw_request_bodies, Mapping), "raw_request_bodies must be a Mapping"
46
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
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
- assert all(isinstance(op, IROperation) for op in ops), "All items must be IROperation objects"
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
- assert isinstance(op, IROperation), "op must be an IROperation"
28
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
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
- assert isinstance(rb_node, Mapping), "rb_node must be a Mapping"
37
- assert isinstance(raw_request_bodies, Mapping), "raw_request_bodies must be a Mapping"
38
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
39
- assert operation_id, "operation_id must be provided"
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
- assert request_body.content == content_map, "Request body content mismatch"
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
- assert isinstance(param_node_data, Mapping), "param_node_data must be a Mapping"
31
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
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
- assert isinstance(node, Mapping), "node must be a Mapping"
66
- assert "name" in node, "Parameter node must have a name"
67
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
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"]
@@ -79,6 +84,29 @@ def parse_parameter(
79
84
  base_param_promo_name = f"{operation_id_for_promo}Param" if operation_id_for_promo else ""
80
85
  name_for_inline_param_schema = f"{base_param_promo_name}{NameSanitizer.sanitize_class_name(param_name)}"
81
86
 
87
+ # General rule: if a parameter is defined inline but a components parameter exists with the
88
+ # same name and location, prefer the components schema (often richer: arrays/enums/refs).
89
+ try:
90
+ if isinstance(context, ParsingContext):
91
+ components_params = context.raw_spec_components.get("parameters", {})
92
+ if isinstance(components_params, Mapping):
93
+ for comp_key, comp_param in components_params.items():
94
+ if not isinstance(comp_param, Mapping):
95
+ continue
96
+ if comp_param.get("name") == param_name and comp_param.get("in") == node.get("in"):
97
+ comp_schema = comp_param.get("schema")
98
+ if isinstance(comp_schema, Mapping):
99
+ # Prefer component schema if inline is missing or clearly less specific
100
+ inline_is_specific = isinstance(sch, Mapping) and (
101
+ sch.get("type") in {"array", "object"} or "$ref" in sch or "enum" in sch
102
+ )
103
+ if not inline_is_specific:
104
+ sch = comp_schema
105
+ break
106
+ except Exception:
107
+ # Be conservative on any unexpected structure
108
+ pass
109
+
82
110
  # For parameters, we want to avoid creating complex schemas for simple enum arrays
83
111
  # Check if this is a simple enum array and handle it specially
84
112
  if (
@@ -91,12 +119,38 @@ def parse_parameter(
91
119
  and "enum" in sch["items"]
92
120
  and "$ref" not in sch["items"]
93
121
  ):
94
- # This is an array of string enums - for parameters, we can treat this as List[str]
95
- # rather than creating complex named schemas
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
+
96
150
  schema_ir = IRSchema(
97
151
  name=None,
98
152
  type="array",
99
- items=IRSchema(name=None, type="string", enum=sch["items"]["enum"]),
153
+ items=items_schema,
100
154
  description=sch.get("description"),
101
155
  )
102
156
  else:
@@ -115,7 +169,9 @@ def parse_parameter(
115
169
  )
116
170
 
117
171
  # Post-condition check
118
- assert param.name == node["name"], "Parameter name mismatch"
119
- assert param.schema is not None, "Parameter schema must be created"
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")
120
176
 
121
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
- assert isinstance(code, str), "code must be a string"
38
- assert isinstance(node, Mapping), "node must be a Mapping"
39
- assert isinstance(context, ParsingContext), "context must be a ParsingContext"
40
- assert operation_id_for_promo, "operation_id_for_promo must be provided"
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
- assert response.status_code == code, "Response status code mismatch"
101
- assert response.content == content, "Response content mismatch"
102
- assert response.stream == stream_flag, "Response stream flag mismatch"
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
- assert isinstance(raw_schemas, dict), "raw_schemas must be a dict"
32
- assert isinstance(raw_components, Mapping), "raw_components must be a Mapping"
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
- assert all(n in context.parsed_schemas for n in raw_schemas), "Not all schemas were parsed"
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
- assert isinstance(schemas, dict), "schemas must be a dict"
59
- assert all(isinstance(s, IRSchema) for s in schemas.values()), "all values must be IRSchema objects"
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
- assert len(schemas) >= original_schema_count, "Schemas count should not decrease"
123
- assert original_schemas.issubset(set(schemas.keys())), "Original schemas should still be present"
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
 
@@ -128,6 +135,8 @@ def extract_inline_array_items(schemas: Dict[str, IRSchema]) -> Dict[str, IRSche
128
135
  def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
129
136
  """Extract inline property enums as unique schemas and update property references.
130
137
 
138
+ Also ensures top-level enum schemas are properly marked for generation.
139
+
131
140
  Contracts:
132
141
  Preconditions:
133
142
  - schemas is a dict of IRSchema objects
@@ -136,9 +145,12 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
136
145
  - All property schemas with enums have proper names
137
146
  - All array item schemas have proper names
138
147
  - No duplicate schema names are created
148
+ - Top-level enum schemas have generation_name set
139
149
  """
140
- assert isinstance(schemas, dict), "schemas must be a dict"
141
- assert all(isinstance(s, IRSchema) for s in schemas.values()), "all values must be IRSchema objects"
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")
142
154
 
143
155
  # Store original schema count for post-condition validation
144
156
  original_schema_count = len(schemas)
@@ -149,6 +161,22 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
149
161
 
150
162
  new_enums = {}
151
163
  for schema_name, schema in list(schemas.items()):
164
+ # Handle top-level enum schemas (those defined directly in components/schemas)
165
+ # These are already enums but need generation_name set
166
+ if schema.enum and schema.type in ["string", "integer", "number"]:
167
+ # This is a top-level enum schema
168
+ # Ensure it has generation_name set (will be properly set by emitter later,
169
+ # but we can set it here to avoid the warning)
170
+ if not hasattr(schema, "generation_name") or not schema.generation_name:
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
+ )
175
+ # Mark this as a properly processed enum by ensuring generation_name is set
176
+ # This serves as the marker that this enum was properly processed
177
+ logger.debug(f"Marked top-level enum schema: {schema_name}")
178
+
179
+ # Extract inline enums from properties
152
180
  for prop_name, prop_schema in list(schema.properties.items()):
153
181
  if prop_schema.enum and not prop_schema.name:
154
182
  enum_name = (
@@ -167,6 +195,7 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
167
195
  enum=copy.deepcopy(prop_schema.enum),
168
196
  description=prop_schema.description or f"Enum for {schema_name}.{prop_name}",
169
197
  )
198
+ enum_schema.generation_name = enum_name # Set generation_name for extracted enums
170
199
  new_enums[enum_name] = enum_schema
171
200
 
172
201
  # Update the original property to reference the extracted enum
@@ -178,7 +207,9 @@ def extract_inline_enums(schemas: Dict[str, IRSchema]) -> Dict[str, IRSchema]:
178
207
  schemas.update(new_enums)
179
208
 
180
209
  # Post-condition checks
181
- assert len(schemas) >= original_schema_count, "Schemas count should not decrease"
182
- assert original_schemas.issubset(set(schemas.keys())), "Original schemas should still be present"
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")
183
214
 
184
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
- assert isinstance(schema_name, str), "schema_name must be a string"
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
- assert isinstance(schema_name, str), "schema_name must be a string"
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
- assert isinstance(node, Mapping) and node, "node must be a non-empty Mapping"
47
- assert isinstance(context, ParsingContext), "context must be a ParsingContext instance"
48
- assert callable(_parse_schema_func), "_parse_schema_func must be callable"
49
- assert isinstance(max_depth, int) and max_depth >= 0, "max_depth must be a non-negative integer"
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
- assert isinstance(any_of_nodes, list), "any_of_nodes must be a list"
42
- assert all(isinstance(n, Mapping) for n in any_of_nodes), "all items in any_of_nodes must be Mappings"
43
- assert isinstance(context, ParsingContext), "context must be a ParsingContext instance"
44
- assert max_depth >= 0, "max_depth must be non-negative"
45
- assert callable(parse_fn), "parse_fn must be a callable"
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
- assert isinstance(context, ParsingContext), "context must be a ParsingContext instance"
55
- assert callable(parse_fn), "parse_fn must be callable"
56
- assert isinstance(max_depth, int) and max_depth >= 0, "max_depth must be a non-negative integer"
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
- assert isinstance(one_of_nodes, list), "one_of_nodes must be a list"
39
- assert all(isinstance(n, Mapping) for n in one_of_nodes), "all items in one_of_nodes must be Mappings"
40
- assert isinstance(context, ParsingContext), "context must be a ParsingContext instance"
41
- assert max_depth >= 0, "max_depth must be non-negative"
42
- assert callable(parse_fn), "parse_fn must be a callable"
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
- assert isinstance(node, Mapping), "node must be a Mapping"
57
- assert isinstance(context, ParsingContext), "context must be a ParsingContext instance"
58
- assert callable(parse_fn), "parse_fn must be callable"
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
- assert context is not None, "Context cannot be None for _parse_schema"
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
- assert isinstance(
381
- schema_node, Mapping
382
- ), f"Schema node for '{schema_name or 'anonymous'}' must be a Mapping (e.g., dict), got {type(schema_node)}"
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
- assert self.visitor is not None, "EndpointVisitor not initialized"
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
- assert (
46
- schema_ir.generation_name is not None
47
- ), f"Schema '{schema_ir.name}' must have generation_name set before file generation."
48
- assert (
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
- assert (
142
- s_schema.generation_name is not None
143
- ), f"Schema '{s_schema.name}' missing generation_name in __init__ generation."
144
- assert (
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
- assert isinstance(spec, IRSpec), "spec must be an IRSpec"
188
- assert output_root, "output_root must be a non-empty string"
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"