pyopenapi-gen 0.14.0__py3-none-any.whl → 0.14.2__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 (80) hide show
  1. pyopenapi_gen/cli.py +3 -3
  2. pyopenapi_gen/context/import_collector.py +10 -10
  3. pyopenapi_gen/context/render_context.py +13 -13
  4. pyopenapi_gen/core/auth/plugins.py +7 -7
  5. pyopenapi_gen/core/http_status_codes.py +2 -4
  6. pyopenapi_gen/core/http_transport.py +19 -19
  7. pyopenapi_gen/core/loader/operations/parser.py +2 -2
  8. pyopenapi_gen/core/loader/operations/request_body.py +3 -3
  9. pyopenapi_gen/core/loader/parameters/parser.py +3 -3
  10. pyopenapi_gen/core/loader/responses/parser.py +2 -2
  11. pyopenapi_gen/core/loader/schemas/extractor.py +4 -4
  12. pyopenapi_gen/core/pagination.py +3 -3
  13. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/list_response.py +3 -3
  14. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py +2 -2
  15. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py +3 -3
  16. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py +3 -3
  17. pyopenapi_gen/core/parsing/common/ref_resolution/resolve_schema_ref.py +2 -2
  18. pyopenapi_gen/core/parsing/common/type_parser.py +2 -3
  19. pyopenapi_gen/core/parsing/context.py +10 -10
  20. pyopenapi_gen/core/parsing/cycle_helpers.py +5 -2
  21. pyopenapi_gen/core/parsing/keywords/all_of_parser.py +5 -5
  22. pyopenapi_gen/core/parsing/keywords/any_of_parser.py +4 -4
  23. pyopenapi_gen/core/parsing/keywords/array_items_parser.py +4 -4
  24. pyopenapi_gen/core/parsing/keywords/one_of_parser.py +4 -4
  25. pyopenapi_gen/core/parsing/keywords/properties_parser.py +5 -5
  26. pyopenapi_gen/core/parsing/schema_finalizer.py +15 -15
  27. pyopenapi_gen/core/parsing/schema_parser.py +44 -25
  28. pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +4 -4
  29. pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py +7 -4
  30. pyopenapi_gen/core/parsing/unified_cycle_detection.py +10 -10
  31. pyopenapi_gen/core/schemas.py +10 -10
  32. pyopenapi_gen/core/streaming_helpers.py +5 -7
  33. pyopenapi_gen/core/telemetry.py +4 -4
  34. pyopenapi_gen/core/utils.py +7 -7
  35. pyopenapi_gen/core/writers/code_writer.py +2 -2
  36. pyopenapi_gen/core/writers/documentation_writer.py +18 -18
  37. pyopenapi_gen/core/writers/line_writer.py +3 -3
  38. pyopenapi_gen/core/writers/python_construct_renderer.py +10 -10
  39. pyopenapi_gen/emit/models_emitter.py +2 -2
  40. pyopenapi_gen/emitters/core_emitter.py +3 -5
  41. pyopenapi_gen/emitters/endpoints_emitter.py +24 -16
  42. pyopenapi_gen/emitters/exceptions_emitter.py +4 -3
  43. pyopenapi_gen/emitters/models_emitter.py +6 -6
  44. pyopenapi_gen/generator/client_generator.py +6 -6
  45. pyopenapi_gen/helpers/endpoint_utils.py +16 -18
  46. pyopenapi_gen/helpers/type_cleaner.py +66 -53
  47. pyopenapi_gen/helpers/type_helper.py +7 -7
  48. pyopenapi_gen/helpers/type_resolution/array_resolver.py +4 -4
  49. pyopenapi_gen/helpers/type_resolution/composition_resolver.py +5 -5
  50. pyopenapi_gen/helpers/type_resolution/finalizer.py +38 -22
  51. pyopenapi_gen/helpers/type_resolution/named_resolver.py +4 -5
  52. pyopenapi_gen/helpers/type_resolution/object_resolver.py +11 -11
  53. pyopenapi_gen/helpers/type_resolution/primitive_resolver.py +1 -2
  54. pyopenapi_gen/helpers/type_resolution/resolver.py +2 -3
  55. pyopenapi_gen/ir.py +32 -34
  56. pyopenapi_gen/types/contracts/protocols.py +5 -5
  57. pyopenapi_gen/types/contracts/types.py +2 -3
  58. pyopenapi_gen/types/resolvers/reference_resolver.py +4 -4
  59. pyopenapi_gen/types/resolvers/response_resolver.py +6 -4
  60. pyopenapi_gen/types/resolvers/schema_resolver.py +32 -16
  61. pyopenapi_gen/types/services/type_service.py +55 -9
  62. pyopenapi_gen/types/strategies/response_strategy.py +6 -7
  63. pyopenapi_gen/visit/client_visitor.py +5 -7
  64. pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +7 -7
  65. pyopenapi_gen/visit/endpoint/generators/request_generator.py +5 -5
  66. pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +38 -17
  67. pyopenapi_gen/visit/endpoint/generators/signature_generator.py +4 -4
  68. pyopenapi_gen/visit/endpoint/generators/url_args_generator.py +17 -17
  69. pyopenapi_gen/visit/endpoint/processors/import_analyzer.py +8 -8
  70. pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +13 -13
  71. pyopenapi_gen/visit/model/alias_generator.py +1 -4
  72. pyopenapi_gen/visit/model/dataclass_generator.py +139 -10
  73. pyopenapi_gen/visit/model/model_visitor.py +2 -3
  74. pyopenapi_gen/visit/visitor.py +3 -3
  75. {pyopenapi_gen-0.14.0.dist-info → pyopenapi_gen-0.14.2.dist-info}/METADATA +1 -1
  76. pyopenapi_gen-0.14.2.dist-info/RECORD +132 -0
  77. pyopenapi_gen-0.14.0.dist-info/RECORD +0 -132
  78. {pyopenapi_gen-0.14.0.dist-info → pyopenapi_gen-0.14.2.dist-info}/WHEEL +0 -0
  79. {pyopenapi_gen-0.14.0.dist-info → pyopenapi_gen-0.14.2.dist-info}/entry_points.txt +0 -0
  80. {pyopenapi_gen-0.14.0.dist-info → pyopenapi_gen-0.14.2.dist-info}/licenses/LICENSE +0 -0
@@ -79,7 +79,7 @@ class ClientVisitor:
79
79
  # For now, the client_py_content check in the test looks for this for ApiKeyAuth specifically:
80
80
  context.add_import(f"{context.core_package_name}.auth.plugins", "ApiKeyAuth")
81
81
 
82
- context.add_typing_imports_for_type("Optional[HttpTransport]")
82
+ context.add_typing_imports_for_type("HttpTransport | None")
83
83
  context.add_typing_imports_for_type("Any")
84
84
  context.add_typing_imports_for_type("Dict")
85
85
  # Class definition
@@ -103,7 +103,7 @@ class ClientVisitor:
103
103
  summary = "Async API client with pluggable transport, tag-specific clients, and client-level headers."
104
104
  args: list[tuple[str, str, str]] = [
105
105
  ("config", "ClientConfig", "Client configuration object."),
106
- ("transport", "Optional[HttpTransport]", "Custom HTTP transport (optional)."),
106
+ ("transport", "HttpTransport | None", "Custom HTTP transport (optional)."),
107
107
  ]
108
108
  for tag, class_name, module_name in tag_tuples:
109
109
  args.append((module_name, class_name, f"Client for '{tag}' endpoints."))
@@ -121,9 +121,7 @@ class ClientVisitor:
121
121
  writer.indent() # Back to class indent (1)
122
122
  writer.write_line('"""')
123
123
  # __init__
124
- writer.write_line(
125
- "def __init__(self, config: ClientConfig, transport: Optional[HttpTransport] = None) -> None:"
126
- )
124
+ writer.write_line("def __init__(self, config: ClientConfig, transport: HttpTransport | None = None) -> None:")
127
125
  writer.indent()
128
126
  writer.write_line("self.config = config")
129
127
  writer.write_line(
@@ -133,8 +131,8 @@ class ClientVisitor:
133
131
  writer.write_line("self._base_url: str = str(self.config.base_url)")
134
132
  # Initialize private fields for each tag client
135
133
  for tag, class_name, module_name in tag_tuples:
136
- context.add_typing_imports_for_type(f"Optional[{class_name}]")
137
- writer.write_line(f"self._{module_name}: Optional[{class_name}] = None")
134
+ context.add_typing_imports_for_type(f"{class_name} | None")
135
+ writer.write_line(f"self._{module_name}: {class_name} | None = None")
138
136
  writer.dedent()
139
137
  writer.write_line("")
140
138
  # @property for each tag client
@@ -6,7 +6,7 @@ from __future__ import annotations
6
6
 
7
7
  import logging
8
8
  import textwrap # For _wrap_docstring logic
9
- from typing import TYPE_CHECKING, Any, Dict, Optional
9
+ from typing import TYPE_CHECKING, Any
10
10
 
11
11
  from pyopenapi_gen.core.writers.code_writer import CodeWriter
12
12
  from pyopenapi_gen.core.writers.documentation_writer import DocumentationBlock, DocumentationWriter
@@ -23,8 +23,8 @@ logger = logging.getLogger(__name__)
23
23
  class EndpointDocstringGenerator:
24
24
  """Generates the Python docstring for an endpoint operation."""
25
25
 
26
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
27
- self.schemas: Dict[str, Any] = schemas or {}
26
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
27
+ self.schemas: dict[str, Any] = schemas or {}
28
28
  self.doc_writer = DocumentationWriter(width=88)
29
29
 
30
30
  def _wrap_docstring(self, prefix: str, text: str, width: int = 88) -> str:
@@ -50,7 +50,7 @@ class EndpointDocstringGenerator:
50
50
  writer: CodeWriter,
51
51
  op: IROperation,
52
52
  context: RenderContext,
53
- primary_content_type: Optional[str],
53
+ primary_content_type: str | None,
54
54
  response_strategy: ResponseStrategy,
55
55
  ) -> None:
56
56
  """Writes the method docstring to the provided CodeWriter."""
@@ -67,10 +67,10 @@ class EndpointDocstringGenerator:
67
67
  body_desc = op.request_body.description or "Request body."
68
68
  # Standardized body parameter names based on content type
69
69
  if primary_content_type == "multipart/form-data":
70
- args.append(("files", "Dict[str, IO[Any]]", body_desc + " (multipart/form-data)"))
70
+ args.append(("files", "dict[str, IO[Any]]", body_desc + " (multipart/form-data)"))
71
71
  elif primary_content_type == "application/x-www-form-urlencoded":
72
- # The type here could be more specific if schema is available, but Dict[str, Any] is a safe default.
73
- args.append(("form_data", "Dict[str, Any]", body_desc + " (x-www-form-urlencoded)"))
72
+ # The type here could be more specific if schema is available, but dict[str, Any] is a safe default.
73
+ args.append(("form_data", "dict[str, Any]", body_desc + " (x-www-form-urlencoded)"))
74
74
  elif primary_content_type == "application/json":
75
75
  body_type = get_request_body_type(op.request_body, context, self.schemas)
76
76
  args.append(("body", body_type, body_desc + " (json)"))
@@ -5,7 +5,7 @@ Helper class for generating the HTTP request call for an endpoint method.
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
- from typing import TYPE_CHECKING, Any, Dict, Optional
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  from pyopenapi_gen.core.writers.code_writer import CodeWriter
11
11
 
@@ -21,8 +21,8 @@ logger = logging.getLogger(__name__)
21
21
  class EndpointRequestGenerator:
22
22
  """Generates the self._transport.request(...) call for an endpoint method."""
23
23
 
24
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
25
- self.schemas: Dict[str, Any] = schemas or {}
24
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
25
+ self.schemas: dict[str, Any] = schemas or {}
26
26
 
27
27
  def generate_request_call(
28
28
  self,
@@ -30,8 +30,8 @@ class EndpointRequestGenerator:
30
30
  op: IROperation,
31
31
  context: RenderContext, # Pass context for potential import needs
32
32
  has_header_params: bool,
33
- primary_content_type: Optional[str],
34
- # resolved_body_type: Optional[str], # May not be directly needed here if logic relies on var names
33
+ primary_content_type: str | None,
34
+ # resolved_body_type: str | None, # May not be directly needed here if logic relies on var names
35
35
  ) -> None:
36
36
  """Writes the self._transport.request call to the CodeWriter."""
37
37
  # Logic from EndpointMethodGenerator._write_request
@@ -5,7 +5,7 @@ Helper class for generating response handling logic for an endpoint method.
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
- from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict
8
+ from typing import TYPE_CHECKING, Any, TypedDict
9
9
 
10
10
  from pyopenapi_gen.core.http_status_codes import get_exception_class_name
11
11
  from pyopenapi_gen.core.writers.code_writer import CodeWriter
@@ -44,8 +44,8 @@ class DefaultCase(TypedDict):
44
44
  class EndpointResponseHandlerGenerator:
45
45
  """Generates the response handling logic for an endpoint method."""
46
46
 
47
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
48
- self.schemas: Dict[str, Any] = schemas or {}
47
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
48
+ self.schemas: dict[str, Any] = schemas or {}
49
49
 
50
50
  def _is_type_alias_to_array(self, type_name: str) -> bool:
51
51
  """
@@ -130,7 +130,7 @@ class EndpointResponseHandlerGenerator:
130
130
  Determine if a type should use BaseSchema deserialization.
131
131
 
132
132
  Args:
133
- type_name: The Python type name (e.g., "User", "List[User]", "Optional[User]")
133
+ type_name: The Python type name (e.g., "User", "List[User]", "User | None")
134
134
 
135
135
  Returns:
136
136
  True if the type should use BaseSchema .from_dict() deserialization
@@ -138,14 +138,14 @@ class EndpointResponseHandlerGenerator:
138
138
  # Extract the base type name from complex types
139
139
  base_type = type_name
140
140
 
141
- # Handle List[Type], Optional[Type], etc.
141
+ # Handle List[Type], Type | None, etc.
142
142
  if "[" in base_type and "]" in base_type:
143
- # Extract the inner type from List[Type], Optional[Type], etc.
143
+ # Extract the inner type from List[Type], Type | None, etc.
144
144
  start_bracket = base_type.find("[")
145
145
  end_bracket = base_type.rfind("]")
146
146
  inner_type = base_type[start_bracket + 1 : end_bracket]
147
147
 
148
- # For Union types like Optional[User] -> Union[User, None], take the first type
148
+ # For Union types like User | None -> Union[User, None], take the first type
149
149
  if ", " in inner_type:
150
150
  inner_type = inner_type.split(", ")[0]
151
151
 
@@ -168,8 +168,9 @@ class EndpointResponseHandlerGenerator:
168
168
  }:
169
169
  return False
170
170
 
171
- # Skip typing constructs (both uppercase and lowercase)
172
- if base_type.startswith(("Dict[", "List[", "Optional[", "Union[", "Tuple[", "dict[", "list[", "tuple[")):
171
+ # Skip typing constructs
172
+ # Note: Modern Python 3.10+ uses | None instead of Optional[X]
173
+ if base_type.startswith(("dict[", "List[", "Union[", "Tuple[", "dict[", "list[", "tuple[")):
173
174
  return False
174
175
 
175
176
  # Check if this is a type alias (array or non-array) - these should NOT use BaseSchema
@@ -180,8 +181,7 @@ class EndpointResponseHandlerGenerator:
180
181
  # Check if it's a model type (contains a dot indicating it's from models package)
181
182
  # or if it's a simple class name that's likely a generated model (starts with uppercase)
182
183
  return "." in base_type or (
183
- base_type[0].isupper()
184
- and base_type not in {"Dict", "List", "Optional", "Union", "Tuple", "dict", "list", "tuple"}
184
+ base_type[0].isupper() and base_type not in {"Dict", "List", "Union", "Tuple", "dict", "list", "tuple"}
185
185
  )
186
186
 
187
187
  def _get_base_schema_deserialization_code(self, return_type: str, data_expr: str) -> str:
@@ -203,8 +203,29 @@ class EndpointResponseHandlerGenerator:
203
203
  item_type = return_type[5:-1] # Remove 'list[' and ']'
204
204
  return f"[{item_type}.from_dict(item) for item in {data_expr}]"
205
205
  elif return_type.startswith("Optional["):
206
- # Handle Optional[Model] types
206
+ # SANITY CHECK: Unified type system should never produce Optional[X]
207
+ logger.error(
208
+ f"❌ ARCHITECTURE VIOLATION: Received legacy Optional[X] type in response handler: {return_type}. "
209
+ f"Unified type system must generate X | None directly."
210
+ )
211
+ # Defensive conversion (but this indicates a serious bug upstream)
207
212
  inner_type = return_type[9:-1] # Remove 'Optional[' and ']'
213
+ logger.warning(f"⚠️ Converting to modern syntax internally for: {inner_type} | None")
214
+
215
+ # Check if inner type is also a list
216
+ if inner_type.startswith("List[") or inner_type.startswith("list["):
217
+ list_code = self._get_base_schema_deserialization_code(inner_type, data_expr)
218
+ return f"{list_code} if {data_expr} is not None else None"
219
+ else:
220
+ return f"{inner_type}.from_dict({data_expr}) if {data_expr} is not None else None"
221
+ elif " | None" in return_type or return_type.endswith("| None"):
222
+ # Handle Model | None types (modern Python 3.10+ syntax)
223
+ # Extract base type from "X | None" pattern
224
+ if " | None" in return_type:
225
+ inner_type = return_type.replace(" | None", "").strip()
226
+ else:
227
+ inner_type = return_type.replace("| None", "").strip()
228
+
208
229
  # Check if inner type is also a list
209
230
  if inner_type.startswith("List[") or inner_type.startswith("list["):
210
231
  list_code = self._get_base_schema_deserialization_code(inner_type, data_expr)
@@ -230,7 +251,7 @@ class EndpointResponseHandlerGenerator:
230
251
  return_type: str,
231
252
  context: RenderContext,
232
253
  op: IROperation,
233
- response_ir: Optional[IRResponse] = None,
254
+ response_ir: IRResponse | None = None,
234
255
  ) -> str:
235
256
  """Determines the code snippet to extract/transform the response body."""
236
257
  # Handle None, StreamingResponse, Iterator, etc.
@@ -243,7 +264,7 @@ class EndpointResponseHandlerGenerator:
243
264
  if return_type == "AsyncIterator[bytes]":
244
265
  context.add_import(f"{context.core_package_name}.streaming_helpers", "iter_bytes")
245
266
  return "iter_bytes(response)"
246
- elif "Dict[str, Any]" in return_type or "dict" in return_type.lower():
267
+ elif "dict[str, Any]" in return_type or "dict" in return_type.lower():
247
268
  # For event streams that return Dict objects
248
269
  context.add_import(f"{context.core_package_name}.streaming_helpers", "iter_sse_events_text")
249
270
  return "sse_json_stream_marker" # Special marker handled by _write_parsed_return
@@ -260,7 +281,7 @@ class EndpointResponseHandlerGenerator:
260
281
  return "iter_bytes(response)"
261
282
 
262
283
  # Special case for "data: Any" unwrapping when the actual schema has no fields/properties
263
- if return_type in {"Dict[str, Any]", "Dict[str, object]", "object", "Any"}:
284
+ if return_type in {"dict[str, Any]", "dict[str, object]", "object", "Any"}:
264
285
  context.add_import("typing", "Dict")
265
286
  context.add_import("typing", "Any")
266
287
 
@@ -273,7 +294,7 @@ class EndpointResponseHandlerGenerator:
273
294
  return "response.json() # Type is Any"
274
295
  elif return_type == "None":
275
296
  return "None" # This will be handled by generate_response_handling directly
276
- else: # Includes schema-defined models, List[], Dict[], Optional[]
297
+ else: # Includes schema-defined models, List[], dict[], Optional[]
277
298
  context.add_typing_imports_for_type(return_type) # Ensure model itself is imported
278
299
 
279
300
  # Check if we should use BaseSchema deserialization instead of cast()
@@ -434,7 +455,7 @@ class EndpointResponseHandlerGenerator:
434
455
  context.add_import("typing", "cast")
435
456
  writer.write_line(f"return cast({strategy.return_type}, response.json())")
436
457
 
437
- def _get_response_schema(self, response_ir: IRResponse) -> Optional[IRSchema]:
458
+ def _get_response_schema(self, response_ir: IRResponse) -> IRSchema | None:
438
459
  """Extract the schema from a response IR."""
439
460
  if not response_ir.content:
440
461
  return None
@@ -5,7 +5,7 @@ Helper class for generating the method signature for an endpoint.
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
8
+ from typing import TYPE_CHECKING, Any, List
9
9
 
10
10
  from pyopenapi_gen.core.utils import NameSanitizer
11
11
  from pyopenapi_gen.core.writers.code_writer import CodeWriter
@@ -24,15 +24,15 @@ logger = logging.getLogger(__name__)
24
24
  class EndpointMethodSignatureGenerator:
25
25
  """Generates the Python method signature for an endpoint operation."""
26
26
 
27
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
28
- self.schemas: Dict[str, Any] = schemas or {}
27
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
28
+ self.schemas: dict[str, Any] = schemas or {}
29
29
 
30
30
  def generate_signature(
31
31
  self,
32
32
  writer: CodeWriter,
33
33
  op: IROperation,
34
34
  context: RenderContext,
35
- ordered_params: List[Dict[str, Any]],
35
+ ordered_params: List[dict[str, Any]],
36
36
  strategy: ResponseStrategy,
37
37
  ) -> None:
38
38
  """Writes the method signature to the provided CodeWriter."""
@@ -6,7 +6,7 @@ from __future__ import annotations
6
6
 
7
7
  import logging
8
8
  import re # For _build_url_with_path_vars
9
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
9
+ from typing import TYPE_CHECKING, Any, List
10
10
 
11
11
  from pyopenapi_gen.core.utils import NameSanitizer
12
12
  from pyopenapi_gen.core.writers.code_writer import CodeWriter
@@ -21,8 +21,8 @@ logger = logging.getLogger(__name__)
21
21
  class EndpointUrlArgsGenerator:
22
22
  """Generates URL, query, and header parameters for an endpoint method."""
23
23
 
24
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
25
- self.schemas: Dict[str, Any] = schemas or {}
24
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
25
+ self.schemas: dict[str, Any] = schemas or {}
26
26
 
27
27
  def _build_url_with_path_vars(self, path: str) -> str:
28
28
  """Builds the f-string for URL construction, substituting path variables."""
@@ -34,7 +34,7 @@ class EndpointUrlArgsGenerator:
34
34
  return f'f"{{self.base_url}}{formatted_path}"'
35
35
 
36
36
  def _write_query_params(
37
- self, writer: CodeWriter, op: IROperation, ordered_params: List[Dict[str, Any]], context: RenderContext
37
+ self, writer: CodeWriter, op: IROperation, ordered_params: List[dict[str, Any]], context: RenderContext
38
38
  ) -> None:
39
39
  """Writes query parameter dictionary construction."""
40
40
  # Logic from EndpointMethodGenerator._write_query_params
@@ -58,7 +58,7 @@ class EndpointUrlArgsGenerator:
58
58
  )
59
59
 
60
60
  def _write_header_params(
61
- self, writer: CodeWriter, op: IROperation, ordered_params: List[Dict[str, Any]], context: RenderContext
61
+ self, writer: CodeWriter, op: IROperation, ordered_params: List[dict[str, Any]], context: RenderContext
62
62
  ) -> None:
63
63
  """Writes header parameter dictionary construction."""
64
64
  # Logic from EndpointMethodGenerator._write_header_params
@@ -89,9 +89,9 @@ class EndpointUrlArgsGenerator:
89
89
  writer: CodeWriter,
90
90
  op: IROperation,
91
91
  context: RenderContext,
92
- ordered_params: List[Dict[str, Any]],
93
- primary_content_type: Optional[str],
94
- resolved_body_type: Optional[str],
92
+ ordered_params: List[dict[str, Any]],
93
+ primary_content_type: str | None,
94
+ resolved_body_type: str | None,
95
95
  ) -> bool:
96
96
  """Writes URL, query, and header parameters. Returns True if header params were written."""
97
97
  # Main logic from EndpointMethodGenerator._write_url_and_args
@@ -103,9 +103,9 @@ class EndpointUrlArgsGenerator:
103
103
  # Check if any parameter in ordered_params is a query param, not just op.parameters
104
104
  has_spec_query_params = any(p.get("param_in") == "query" for p in ordered_params)
105
105
  if has_spec_query_params:
106
- context.add_import("typing", "Any") # For Dict[str, Any]
107
- context.add_import("typing", "Dict") # For Dict[str, Any]
108
- writer.write_line("params: Dict[str, Any] = {")
106
+ context.add_import("typing", "Any") # For dict[str, Any]
107
+ context.add_import("typing", "Dict") # For dict[str, Any]
108
+ writer.write_line("params: dict[str, Any] = {")
109
109
  # writer.indent() # Indentation should be handled by CodeWriter when writing lines
110
110
  self._write_query_params(writer, op, ordered_params, context)
111
111
  # writer.dedent()
@@ -115,9 +115,9 @@ class EndpointUrlArgsGenerator:
115
115
  # Header Parameters
116
116
  has_header_params = any(p.get("param_in") == "header" for p in ordered_params)
117
117
  if has_header_params:
118
- context.add_import("typing", "Any") # For Dict[str, Any]
119
- context.add_import("typing", "Dict") # For Dict[str, Any]
120
- writer.write_line("headers: Dict[str, Any] = {")
118
+ context.add_import("typing", "Any") # For dict[str, Any]
119
+ context.add_import("typing", "Dict") # For dict[str, Any]
120
+ writer.write_line("headers: dict[str, Any] = {")
121
121
  # writer.indent()
122
122
  self._write_header_params(writer, op, ordered_params, context)
123
123
  # writer.dedent()
@@ -160,11 +160,11 @@ class EndpointUrlArgsGenerator:
160
160
  context.add_import("typing", "IO") # For IO[Any]
161
161
  context.add_import("typing", "Any")
162
162
  writer.write_line(
163
- "files_data: Dict[str, IO[Any]] = DataclassSerializer.serialize(files) # type failed"
163
+ "files_data: dict[str, IO[Any]] = DataclassSerializer.serialize(files) # type failed"
164
164
  )
165
165
  elif primary_content_type == "application/x-www-form-urlencoded":
166
166
  # form_data is the expected parameter name from EndpointParameterProcessor
167
- # resolved_body_type should be Dict[str, Any]
167
+ # resolved_body_type should be dict[str, Any]
168
168
  if resolved_body_type:
169
169
  writer.write_line(
170
170
  f"form_data_body: {resolved_body_type} = DataclassSerializer.serialize(form_data)"
@@ -173,7 +173,7 @@ class EndpointUrlArgsGenerator:
173
173
  context.add_import("typing", "Dict")
174
174
  context.add_import("typing", "Any")
175
175
  writer.write_line(
176
- "form_data_body: Dict[str, Any] = DataclassSerializer.serialize(form_data) # Fallback type"
176
+ "form_data_body: dict[str, Any] = DataclassSerializer.serialize(form_data) # Fallback type"
177
177
  )
178
178
  elif resolved_body_type == "bytes": # e.g. application/octet-stream
179
179
  # bytes_content is the expected parameter name from EndpointParameterProcessor
@@ -5,7 +5,7 @@ Helper class for analyzing an IROperation and registering necessary imports.
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
- from typing import TYPE_CHECKING, Any, Dict, Optional # IO for multipart type hint
8
+ from typing import TYPE_CHECKING, Any # IO for multipart type hint
9
9
 
10
10
  # Necessary helpers for type analysis
11
11
  from pyopenapi_gen.helpers.endpoint_utils import (
@@ -24,8 +24,8 @@ logger = logging.getLogger(__name__)
24
24
  class EndpointImportAnalyzer:
25
25
  """Analyzes an IROperation to determine and register required imports."""
26
26
 
27
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
28
- self.schemas: Dict[str, Any] = schemas or {}
27
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
28
+ self.schemas: dict[str, Any] = schemas or {}
29
29
 
30
30
  def analyze_and_register_imports(
31
31
  self,
@@ -40,21 +40,21 @@ class EndpointImportAnalyzer:
40
40
 
41
41
  if op.request_body:
42
42
  content_types = op.request_body.content.keys()
43
- body_param_type: Optional[str] = None
43
+ body_param_type: str | None = None
44
44
  if "multipart/form-data" in content_types:
45
- # Type for multipart is Dict[str, IO[Any]] which requires IO and Any
45
+ # Type for multipart is dict[str, IO[Any]] which requires IO and Any
46
46
  context.add_import("typing", "Dict")
47
47
  context.add_import("typing", "IO")
48
48
  context.add_import("typing", "Any")
49
- # The actual type string "Dict[str, IO[Any]]" will be handled by add_typing_imports_for_type if passed
49
+ # The actual type string "dict[str, IO[Any]]" will be handled by add_typing_imports_for_type if passed
50
50
  # but ensuring components are imported is key.
51
- body_param_type = "Dict[str, IO[Any]]"
51
+ body_param_type = "dict[str, IO[Any]]"
52
52
  elif "application/json" in content_types:
53
53
  body_param_type = get_request_body_type(op.request_body, context, self.schemas)
54
54
  elif "application/x-www-form-urlencoded" in content_types:
55
55
  context.add_import("typing", "Dict")
56
56
  context.add_import("typing", "Any")
57
- body_param_type = "Dict[str, Any]"
57
+ body_param_type = "dict[str, Any]"
58
58
  elif content_types: # Fallback for other types like application/octet-stream
59
59
  body_param_type = "bytes"
60
60
 
@@ -5,7 +5,7 @@ Helper class for processing parameters for an endpoint method.
5
5
  from __future__ import annotations
6
6
 
7
7
  import logging
8
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
8
+ from typing import TYPE_CHECKING, Any, List, Tuple
9
9
 
10
10
  from pyopenapi_gen.core.utils import NameSanitizer
11
11
  from pyopenapi_gen.helpers.endpoint_utils import get_param_type, get_request_body_type
@@ -24,12 +24,12 @@ class EndpointParameterProcessor:
24
24
  method parameters for the endpoint signature and further processing.
25
25
  """
26
26
 
27
- def __init__(self, schemas: Optional[Dict[str, Any]] = None) -> None:
28
- self.schemas: Dict[str, Any] = schemas or {}
27
+ def __init__(self, schemas: dict[str, Any] | None = None) -> None:
28
+ self.schemas: dict[str, Any] = schemas or {}
29
29
 
30
30
  def process_parameters(
31
31
  self, op: IROperation, context: RenderContext
32
- ) -> Tuple[List[Dict[str, Any]], Optional[str], Optional[str]]:
32
+ ) -> Tuple[List[dict[str, Any]], str | None, str | None]:
33
33
  """
34
34
  Prepares and orders parameters for an endpoint method, including path,
35
35
  query, header, and request body parameters.
@@ -40,8 +40,8 @@ class EndpointParameterProcessor:
40
40
  - primary_content_type: The dominant content type for the request body.
41
41
  - resolved_body_type: The Python type hint for the request body.
42
42
  """
43
- ordered_params: List[Dict[str, Any]] = []
44
- param_details_map: Dict[str, Dict[str, Any]] = {}
43
+ ordered_params: List[dict[str, Any]] = []
44
+ param_details_map: dict[str, dict[str, Any]] = {}
45
45
 
46
46
  for param in op.parameters:
47
47
  param_name_sanitized = NameSanitizer.sanitize_method_name(param.name)
@@ -56,21 +56,21 @@ class EndpointParameterProcessor:
56
56
  ordered_params.append(param_info)
57
57
  param_details_map[param_name_sanitized] = param_info
58
58
 
59
- primary_content_type: Optional[str] = None
60
- resolved_body_type: Optional[str] = None
59
+ primary_content_type: str | None = None
60
+ resolved_body_type: str | None = None
61
61
 
62
62
  if op.request_body:
63
63
  content_types = op.request_body.content.keys()
64
64
  body_param_name = "body" # Default name
65
65
  context.add_import("typing", "Any") # General fallback
66
- body_specific_param_info: Optional[Dict[str, Any]] = None
66
+ body_specific_param_info: dict[str, Any] | None = None
67
67
 
68
68
  if "multipart/form-data" in content_types:
69
69
  primary_content_type = "multipart/form-data"
70
70
  body_param_name = "files"
71
71
  context.add_import("typing", "Dict")
72
72
  context.add_import("typing", "IO")
73
- resolved_body_type = "Dict[str, IO[Any]]"
73
+ resolved_body_type = "dict[str, IO[Any]]"
74
74
  body_specific_param_info = {
75
75
  "name": body_param_name,
76
76
  "type": resolved_body_type,
@@ -95,7 +95,7 @@ class EndpointParameterProcessor:
95
95
  primary_content_type = "application/x-www-form-urlencoded"
96
96
  body_param_name = "form_data"
97
97
  context.add_import("typing", "Dict")
98
- resolved_body_type = "Dict[str, Any]"
98
+ resolved_body_type = "dict[str, Any]"
99
99
  body_specific_param_info = {
100
100
  "name": body_param_name,
101
101
  "type": resolved_body_type,
@@ -138,8 +138,8 @@ class EndpointParameterProcessor:
138
138
  return final_ordered_params, primary_content_type, resolved_body_type
139
139
 
140
140
  def _ensure_path_variables_as_params(
141
- self, op: IROperation, current_params: List[Dict[str, Any]], param_details_map: Dict[str, Dict[str, Any]]
142
- ) -> List[Dict[str, Any]]:
141
+ self, op: IROperation, current_params: List[dict[str, Any]], param_details_map: dict[str, dict[str, Any]]
142
+ ) -> List[dict[str, Any]]:
143
143
  """
144
144
  Ensures that all variables in the URL path are present in the list of parameters.
145
145
  If a path variable is not already defined as a parameter, it's added as a required string type.
@@ -3,13 +3,11 @@ Generates Python code for type aliases from IRSchema objects.
3
3
  """
4
4
 
5
5
  import logging
6
- from typing import Dict, Optional
7
6
 
8
7
  from pyopenapi_gen import IRSchema
9
8
  from pyopenapi_gen.context.render_context import RenderContext
10
9
  from pyopenapi_gen.core.utils import NameSanitizer
11
10
  from pyopenapi_gen.core.writers.python_construct_renderer import PythonConstructRenderer
12
- from pyopenapi_gen.helpers.type_resolution.finalizer import TypeFinalizer
13
11
  from pyopenapi_gen.types.services.type_service import UnifiedTypeService
14
12
 
15
13
  logger = logging.getLogger(__name__)
@@ -21,7 +19,7 @@ class AliasGenerator:
21
19
  def __init__(
22
20
  self,
23
21
  renderer: PythonConstructRenderer,
24
- all_schemas: Optional[Dict[str, IRSchema]],
22
+ all_schemas: dict[str, IRSchema] | None,
25
23
  ):
26
24
  # Pre-condition
27
25
  if renderer is None:
@@ -70,7 +68,6 @@ class AliasGenerator:
70
68
 
71
69
  alias_name = NameSanitizer.sanitize_class_name(base_name)
72
70
  target_type = self.type_service.resolve_schema_type(schema, context, required=True, resolve_underlying=True)
73
- target_type = TypeFinalizer(context)._clean_type(target_type)
74
71
 
75
72
  # logger.debug(f"AliasGenerator: Rendering alias '{alias_name}' for target type '{target_type}'.")
76
73