pyopenapi-gen 0.13.0__py3-none-any.whl → 0.14.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyopenapi_gen/cli.py +3 -3
- pyopenapi_gen/context/import_collector.py +10 -10
- pyopenapi_gen/context/render_context.py +13 -13
- pyopenapi_gen/core/auth/plugins.py +7 -7
- pyopenapi_gen/core/http_status_codes.py +218 -0
- pyopenapi_gen/core/http_transport.py +19 -19
- pyopenapi_gen/core/loader/operations/parser.py +2 -2
- pyopenapi_gen/core/loader/operations/request_body.py +3 -3
- pyopenapi_gen/core/loader/parameters/parser.py +3 -3
- pyopenapi_gen/core/loader/responses/parser.py +2 -2
- pyopenapi_gen/core/loader/schemas/extractor.py +4 -4
- pyopenapi_gen/core/pagination.py +3 -3
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/list_response.py +3 -3
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py +2 -2
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py +3 -3
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py +3 -3
- pyopenapi_gen/core/parsing/common/ref_resolution/resolve_schema_ref.py +2 -2
- pyopenapi_gen/core/parsing/common/type_parser.py +2 -3
- pyopenapi_gen/core/parsing/context.py +10 -10
- pyopenapi_gen/core/parsing/cycle_helpers.py +5 -2
- pyopenapi_gen/core/parsing/keywords/all_of_parser.py +5 -5
- pyopenapi_gen/core/parsing/keywords/any_of_parser.py +4 -4
- pyopenapi_gen/core/parsing/keywords/array_items_parser.py +4 -4
- pyopenapi_gen/core/parsing/keywords/one_of_parser.py +4 -4
- pyopenapi_gen/core/parsing/keywords/properties_parser.py +5 -5
- pyopenapi_gen/core/parsing/schema_finalizer.py +15 -15
- pyopenapi_gen/core/parsing/schema_parser.py +44 -25
- pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +4 -4
- pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py +7 -4
- pyopenapi_gen/core/parsing/unified_cycle_detection.py +10 -10
- pyopenapi_gen/core/postprocess_manager.py +85 -12
- pyopenapi_gen/core/schemas.py +10 -10
- pyopenapi_gen/core/streaming_helpers.py +5 -7
- pyopenapi_gen/core/telemetry.py +4 -4
- pyopenapi_gen/core/utils.py +7 -7
- pyopenapi_gen/core/writers/code_writer.py +2 -2
- pyopenapi_gen/core/writers/documentation_writer.py +18 -18
- pyopenapi_gen/core/writers/line_writer.py +3 -3
- pyopenapi_gen/core/writers/python_construct_renderer.py +15 -11
- pyopenapi_gen/emit/models_emitter.py +2 -2
- pyopenapi_gen/emitters/core_emitter.py +3 -5
- pyopenapi_gen/emitters/endpoints_emitter.py +12 -12
- pyopenapi_gen/emitters/exceptions_emitter.py +153 -18
- pyopenapi_gen/emitters/models_emitter.py +6 -6
- pyopenapi_gen/generator/client_generator.py +10 -8
- pyopenapi_gen/helpers/endpoint_utils.py +16 -18
- pyopenapi_gen/helpers/type_cleaner.py +66 -53
- pyopenapi_gen/helpers/type_helper.py +7 -7
- pyopenapi_gen/helpers/type_resolution/array_resolver.py +4 -4
- pyopenapi_gen/helpers/type_resolution/composition_resolver.py +5 -5
- pyopenapi_gen/helpers/type_resolution/finalizer.py +38 -22
- pyopenapi_gen/helpers/type_resolution/named_resolver.py +4 -5
- pyopenapi_gen/helpers/type_resolution/object_resolver.py +11 -11
- pyopenapi_gen/helpers/type_resolution/primitive_resolver.py +1 -2
- pyopenapi_gen/helpers/type_resolution/resolver.py +2 -3
- pyopenapi_gen/ir.py +32 -34
- pyopenapi_gen/types/contracts/protocols.py +5 -5
- pyopenapi_gen/types/contracts/types.py +2 -3
- pyopenapi_gen/types/resolvers/reference_resolver.py +4 -4
- pyopenapi_gen/types/resolvers/response_resolver.py +6 -4
- pyopenapi_gen/types/resolvers/schema_resolver.py +32 -16
- pyopenapi_gen/types/services/type_service.py +55 -9
- pyopenapi_gen/types/strategies/response_strategy.py +6 -7
- pyopenapi_gen/visit/client_visitor.py +5 -7
- pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +7 -7
- pyopenapi_gen/visit/endpoint/generators/request_generator.py +5 -5
- pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +41 -19
- pyopenapi_gen/visit/endpoint/generators/signature_generator.py +4 -4
- pyopenapi_gen/visit/endpoint/generators/url_args_generator.py +17 -17
- pyopenapi_gen/visit/endpoint/processors/import_analyzer.py +8 -8
- pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +13 -13
- pyopenapi_gen/visit/exception_visitor.py +54 -16
- pyopenapi_gen/visit/model/alias_generator.py +1 -4
- pyopenapi_gen/visit/model/dataclass_generator.py +139 -10
- pyopenapi_gen/visit/model/model_visitor.py +2 -3
- pyopenapi_gen/visit/visitor.py +3 -3
- {pyopenapi_gen-0.13.0.dist-info → pyopenapi_gen-0.14.1.dist-info}/METADATA +1 -1
- pyopenapi_gen-0.14.1.dist-info/RECORD +132 -0
- pyopenapi_gen-0.13.0.dist-info/RECORD +0 -131
- {pyopenapi_gen-0.13.0.dist-info → pyopenapi_gen-0.14.1.dist-info}/WHEEL +0 -0
- {pyopenapi_gen-0.13.0.dist-info → pyopenapi_gen-0.14.1.dist-info}/entry_points.txt +0 -0
- {pyopenapi_gen-0.13.0.dist-info → pyopenapi_gen-0.14.1.dist-info}/licenses/LICENSE +0 -0
@@ -3,7 +3,7 @@ Module for handling missing schema references.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any, Callable, Mapping
|
6
|
+
from typing import Any, Callable, Mapping
|
7
7
|
|
8
8
|
from pyopenapi_gen.ir import IRSchema
|
9
9
|
|
@@ -19,7 +19,7 @@ def handle_missing_ref(
|
|
19
19
|
ref_name: str,
|
20
20
|
context: ParsingContext,
|
21
21
|
max_depth: int,
|
22
|
-
parse_fn: Callable[[
|
22
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
23
23
|
) -> IRSchema:
|
24
24
|
"""
|
25
25
|
Handles a missing schema reference by attempting fallback strategies.
|
@@ -3,7 +3,7 @@ Module for handling new schema references.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any, Callable,
|
6
|
+
from typing import Any, Callable, Mapping
|
7
7
|
|
8
8
|
from pyopenapi_gen.ir import IRSchema
|
9
9
|
|
@@ -15,10 +15,10 @@ logger = logging.getLogger(__name__)
|
|
15
15
|
|
16
16
|
def parse_new_schema(
|
17
17
|
ref_name: str,
|
18
|
-
node_data:
|
18
|
+
node_data: dict[str, Any],
|
19
19
|
context: ParsingContext,
|
20
20
|
max_depth: int,
|
21
|
-
parse_fn: Callable[[
|
21
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
22
22
|
) -> IRSchema:
|
23
23
|
"""
|
24
24
|
Parses a new schema from raw data.
|
@@ -3,7 +3,7 @@ Module for handling stripped suffix fallback strategy.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any, Callable, Mapping
|
6
|
+
from typing import Any, Callable, Mapping
|
7
7
|
|
8
8
|
from pyopenapi_gen.ir import IRSchema
|
9
9
|
|
@@ -17,8 +17,8 @@ def try_stripped_suffix_fallback(
|
|
17
17
|
ref_value: str,
|
18
18
|
context: ParsingContext,
|
19
19
|
max_depth: int,
|
20
|
-
parse_fn: Callable[[
|
21
|
-
) ->
|
20
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
21
|
+
) -> IRSchema | None:
|
22
22
|
"""
|
23
23
|
Attempts to resolve a reference by stripping common suffixes.
|
24
24
|
|
@@ -3,7 +3,7 @@ Main module for schema reference resolution.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
from typing import Any, Callable, Mapping
|
6
|
+
from typing import Any, Callable, Mapping
|
7
7
|
|
8
8
|
from pyopenapi_gen.ir import IRSchema
|
9
9
|
|
@@ -21,7 +21,7 @@ def resolve_schema_ref(
|
|
21
21
|
ref_name: str,
|
22
22
|
context: ParsingContext,
|
23
23
|
max_depth: int,
|
24
|
-
_parse_schema: Callable[[
|
24
|
+
_parse_schema: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
25
25
|
) -> IRSchema:
|
26
26
|
"""
|
27
27
|
Resolves a schema reference in an OpenAPI specification.
|
@@ -7,7 +7,6 @@ from __future__ import annotations
|
|
7
7
|
from typing import (
|
8
8
|
Any,
|
9
9
|
List,
|
10
|
-
Optional,
|
11
10
|
Tuple,
|
12
11
|
)
|
13
12
|
|
@@ -15,8 +14,8 @@ from typing import (
|
|
15
14
|
|
16
15
|
|
17
16
|
def extract_primary_type_and_nullability(
|
18
|
-
type_node: Any, schema_name:
|
19
|
-
) -> Tuple[
|
17
|
+
type_node: Any, schema_name: str | None = None
|
18
|
+
) -> Tuple[str | None, bool, List[str]]:
|
20
19
|
"""Extract the primary type and nullability from a schema's 'type' field.
|
21
20
|
|
22
21
|
Contracts:
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
7
7
|
import logging
|
8
8
|
import os
|
9
9
|
from dataclasses import dataclass, field
|
10
|
-
from typing import TYPE_CHECKING, Any,
|
10
|
+
from typing import TYPE_CHECKING, Any, List, Mapping, Set, Tuple
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
13
|
from pyopenapi_gen import IRSchema
|
@@ -21,12 +21,12 @@ logger = logging.getLogger(__name__)
|
|
21
21
|
class ParsingContext:
|
22
22
|
"""Manages shared state and context during the schema parsing process."""
|
23
23
|
|
24
|
-
raw_spec_schemas:
|
24
|
+
raw_spec_schemas: dict[str, Mapping[str, Any]] = field(default_factory=dict)
|
25
25
|
raw_spec_components: Mapping[str, Any] = field(default_factory=dict)
|
26
|
-
parsed_schemas:
|
26
|
+
parsed_schemas: dict[str, IRSchema] = field(default_factory=dict)
|
27
27
|
visited_refs: Set[str] = field(default_factory=set)
|
28
28
|
global_schema_names: Set[str] = field(default_factory=set)
|
29
|
-
package_root_name:
|
29
|
+
package_root_name: str | None = None
|
30
30
|
# name_sanitizer: NameSanitizer = field(default_factory=NameSanitizer) # Decided to instantiate where needed for now
|
31
31
|
collected_warnings: List[str] = field(default_factory=list) # For collecting warnings from helpers
|
32
32
|
|
@@ -51,7 +51,7 @@ class ParsingContext:
|
|
51
51
|
max_depth=max_depth, # Share the same parsed_schemas dict
|
52
52
|
)
|
53
53
|
|
54
|
-
def unified_enter_schema(self, schema_name:
|
54
|
+
def unified_enter_schema(self, schema_name: str | None) -> Any:
|
55
55
|
"""Enter schema using unified cycle detection system."""
|
56
56
|
from .unified_cycle_detection import unified_enter_schema
|
57
57
|
|
@@ -64,7 +64,7 @@ class ParsingContext:
|
|
64
64
|
|
65
65
|
return result
|
66
66
|
|
67
|
-
def unified_exit_schema(self, schema_name:
|
67
|
+
def unified_exit_schema(self, schema_name: str | None) -> None:
|
68
68
|
"""Exit schema using unified cycle detection system."""
|
69
69
|
from .unified_cycle_detection import unified_exit_schema
|
70
70
|
|
@@ -89,7 +89,7 @@ class ParsingContext:
|
|
89
89
|
self.unified_cycle_context.depth_exceeded_schemas.clear()
|
90
90
|
self.unified_cycle_context.cycle_detected = False
|
91
91
|
|
92
|
-
def enter_schema(self, schema_name:
|
92
|
+
def enter_schema(self, schema_name: str | None) -> Tuple[bool, str | None]:
|
93
93
|
self.recursion_depth += 1
|
94
94
|
|
95
95
|
if schema_name is None:
|
@@ -113,7 +113,7 @@ class ParsingContext:
|
|
113
113
|
self.currently_parsing.append(schema_name)
|
114
114
|
return False, None
|
115
115
|
|
116
|
-
def exit_schema(self, schema_name:
|
116
|
+
def exit_schema(self, schema_name: str | None) -> None:
|
117
117
|
if self.recursion_depth == 0:
|
118
118
|
self.logger.error("Cannot exit schema: recursion depth would go below zero.")
|
119
119
|
return
|
@@ -149,7 +149,7 @@ class ParsingContext:
|
|
149
149
|
"""Helper to get a string representation of the current parsing path for logs."""
|
150
150
|
return " -> ".join(self.currently_parsing)
|
151
151
|
|
152
|
-
def get_parsed_schemas_for_emitter(self) ->
|
152
|
+
def get_parsed_schemas_for_emitter(self) -> dict[str, IRSchema]:
|
153
153
|
# ---- START RESTORE ----
|
154
154
|
return {
|
155
155
|
name: schema
|
@@ -173,7 +173,7 @@ class ParsingContext:
|
|
173
173
|
raise TypeError("schema_name must be a string")
|
174
174
|
return schema_name in self.parsed_schemas
|
175
175
|
|
176
|
-
def get_parsed_schema(self, schema_name: str) ->
|
176
|
+
def get_parsed_schema(self, schema_name: str) -> "IRSchema" | None:
|
177
177
|
"""Get a parsed schema by its name.
|
178
178
|
|
179
179
|
Contracts:
|
@@ -1,5 +1,8 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
pass
|
3
6
|
|
4
7
|
from pyopenapi_gen import IRSchema
|
5
8
|
from pyopenapi_gen.core.utils import NameSanitizer
|
@@ -77,7 +80,7 @@ def _handle_cycle_detection(
|
|
77
80
|
return schema
|
78
81
|
|
79
82
|
|
80
|
-
def _handle_max_depth_exceeded(original_name:
|
83
|
+
def _handle_max_depth_exceeded(original_name: str | None, context: ParsingContext, max_depth: int) -> IRSchema:
|
81
84
|
"""Handle case where maximum recursion depth is exceeded.
|
82
85
|
|
83
86
|
Contracts:
|
@@ -6,7 +6,7 @@ Renamed from all_of_merger to all_of_parser for consistency.
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import os
|
9
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Mapping, Set, Tuple
|
10
10
|
|
11
11
|
from pyopenapi_gen import IRSchema
|
12
12
|
|
@@ -20,11 +20,11 @@ if TYPE_CHECKING:
|
|
20
20
|
|
21
21
|
def _process_all_of(
|
22
22
|
node: Mapping[str, Any],
|
23
|
-
current_schema_name:
|
23
|
+
current_schema_name: str | None,
|
24
24
|
context: ParsingContext,
|
25
|
-
_parse_schema_func: Callable[[
|
25
|
+
_parse_schema_func: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int | None], IRSchema],
|
26
26
|
max_depth: int = ENV_MAX_DEPTH,
|
27
|
-
) -> Tuple[
|
27
|
+
) -> Tuple[dict[str, IRSchema], Set[str], List[IRSchema]]:
|
28
28
|
"""Processes the 'allOf' keyword in a schema node.
|
29
29
|
|
30
30
|
Merges properties and required fields from all sub-schemas listed in 'allOf'
|
@@ -54,7 +54,7 @@ def _process_all_of(
|
|
54
54
|
|
55
55
|
parsed_all_of_components: List[IRSchema] = []
|
56
56
|
merged_required: Set[str] = set(node.get("required", []))
|
57
|
-
merged_properties:
|
57
|
+
merged_properties: dict[str, IRSchema] = {}
|
58
58
|
|
59
59
|
if "allOf" not in node:
|
60
60
|
current_node_direct_properties = node.get("properties", {})
|
@@ -4,7 +4,7 @@ Parser for 'anyOf' keyword in OpenAPI schemas.
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable, List, Mapping
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Mapping
|
8
8
|
|
9
9
|
from pyopenapi_gen import IRSchema # Main IR model
|
10
10
|
|
@@ -21,9 +21,9 @@ def _parse_any_of_schemas(
|
|
21
21
|
context: ParsingContext,
|
22
22
|
max_depth: int,
|
23
23
|
parse_fn: Callable[ # Accepts the main schema parsing function
|
24
|
-
[
|
24
|
+
[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema
|
25
25
|
],
|
26
|
-
) -> tuple[
|
26
|
+
) -> tuple[List[IRSchema] | None, bool, str | None]:
|
27
27
|
"""Parses 'anyOf' sub-schemas using a provided parsing function.
|
28
28
|
|
29
29
|
Contracts:
|
@@ -51,7 +51,7 @@ def _parse_any_of_schemas(
|
|
51
51
|
|
52
52
|
parsed_schemas_list: List[IRSchema] = [] # Renamed to avoid confusion with module name
|
53
53
|
is_nullable_from_any_of = False
|
54
|
-
effective_schema_type:
|
54
|
+
effective_schema_type: str | None = None
|
55
55
|
|
56
56
|
for sub_node in any_of_nodes:
|
57
57
|
if isinstance(sub_node, dict) and sub_node.get("type") == "null":
|
@@ -5,7 +5,7 @@ Renamed from array_parser.py for clarity.
|
|
5
5
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
|
-
from typing import TYPE_CHECKING, Any, Callable, Mapping
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping
|
9
9
|
|
10
10
|
from pyopenapi_gen import IRSchema
|
11
11
|
|
@@ -18,14 +18,14 @@ if TYPE_CHECKING:
|
|
18
18
|
|
19
19
|
|
20
20
|
def _parse_array_items_schema(
|
21
|
-
parent_schema_name:
|
21
|
+
parent_schema_name: str | None,
|
22
22
|
items_node_data: Mapping[str, Any],
|
23
23
|
context: ParsingContext,
|
24
24
|
parse_fn: Callable[ # Accepts the main schema parsing function
|
25
|
-
[
|
25
|
+
[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema
|
26
26
|
],
|
27
27
|
max_depth: int,
|
28
|
-
) ->
|
28
|
+
) -> IRSchema | None:
|
29
29
|
"""Parses the 'items' sub-schema of an array.
|
30
30
|
|
31
31
|
Args:
|
@@ -4,7 +4,7 @@ Parser for 'oneOf' keyword in OpenAPI schemas.
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable, List, Mapping
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Mapping
|
8
8
|
|
9
9
|
from pyopenapi_gen import IRSchema
|
10
10
|
|
@@ -19,8 +19,8 @@ def _parse_one_of_schemas(
|
|
19
19
|
one_of_nodes: List[Mapping[str, Any]],
|
20
20
|
context: ParsingContext,
|
21
21
|
max_depth: int,
|
22
|
-
parse_fn: Callable[[
|
23
|
-
) -> tuple[
|
22
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
23
|
+
) -> tuple[List[IRSchema] | None, bool, str | None]:
|
24
24
|
"""Parses 'oneOf' sub-schemas using a provided parsing function.
|
25
25
|
|
26
26
|
Contracts:
|
@@ -48,7 +48,7 @@ def _parse_one_of_schemas(
|
|
48
48
|
|
49
49
|
parsed_schemas_list: List[IRSchema] = []
|
50
50
|
is_nullable_from_one_of = False
|
51
|
-
effective_schema_type:
|
51
|
+
effective_schema_type: str | None = None
|
52
52
|
|
53
53
|
for sub_node in one_of_nodes:
|
54
54
|
if isinstance(sub_node, dict) and sub_node.get("type") == "null":
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import logging
|
4
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Mapping
|
5
5
|
|
6
6
|
# Import NameSanitizer for use in name generation
|
7
7
|
from pyopenapi_gen.core.utils import NameSanitizer
|
@@ -27,12 +27,12 @@ if TYPE_CHECKING:
|
|
27
27
|
|
28
28
|
def _parse_properties(
|
29
29
|
properties_node: Mapping[str, Any],
|
30
|
-
parent_schema_name:
|
30
|
+
parent_schema_name: str | None,
|
31
31
|
context: ParsingContext,
|
32
32
|
max_depth: int,
|
33
|
-
parse_fn: Callable[[
|
33
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int | None], IRSchema],
|
34
34
|
logger: logging.Logger,
|
35
|
-
) ->
|
35
|
+
) -> dict[str, IRSchema]:
|
36
36
|
"""Parse properties from a schema node.
|
37
37
|
|
38
38
|
Contracts:
|
@@ -45,7 +45,7 @@ def _parse_properties(
|
|
45
45
|
- Returns a dictionary mapping property names to IRSchema instances
|
46
46
|
- Property references are properly maintained
|
47
47
|
"""
|
48
|
-
properties_map:
|
48
|
+
properties_map: dict[str, IRSchema] = {}
|
49
49
|
|
50
50
|
for prop_key, prop_schema_node in properties_node.items():
|
51
51
|
# Skip invalid property names
|
@@ -4,7 +4,7 @@ Finalization an IRSchema object during OpenAPI parsing.
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Mapping, Set, Union
|
8
8
|
|
9
9
|
from pyopenapi_gen import IRSchema
|
10
10
|
from pyopenapi_gen.core.utils import NameSanitizer
|
@@ -21,24 +21,24 @@ if TYPE_CHECKING:
|
|
21
21
|
|
22
22
|
|
23
23
|
def _finalize_schema_object(
|
24
|
-
name:
|
24
|
+
name: str | None,
|
25
25
|
node: Mapping[str, Any],
|
26
26
|
context: ParsingContext,
|
27
|
-
schema_type:
|
27
|
+
schema_type: str | None,
|
28
28
|
is_nullable: bool,
|
29
|
-
any_of_schemas:
|
30
|
-
one_of_schemas:
|
31
|
-
parsed_all_of_components:
|
32
|
-
final_properties_map:
|
29
|
+
any_of_schemas: List[IRSchema] | None,
|
30
|
+
one_of_schemas: List[IRSchema] | None,
|
31
|
+
parsed_all_of_components: List[IRSchema] | None,
|
32
|
+
final_properties_map: dict[str, IRSchema],
|
33
33
|
merged_required_set: Set[str],
|
34
|
-
final_items_schema:
|
35
|
-
additional_properties_node:
|
36
|
-
enum_node:
|
37
|
-
format_node:
|
38
|
-
description_node:
|
34
|
+
final_items_schema: IRSchema | None,
|
35
|
+
additional_properties_node: Union[bool, Mapping[str, Any]] | None,
|
36
|
+
enum_node: List[Any] | None,
|
37
|
+
format_node: str | None,
|
38
|
+
description_node: str | None,
|
39
39
|
from_unresolved_ref_node: bool,
|
40
40
|
max_depth: int,
|
41
|
-
parse_fn: Callable[[
|
41
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int], IRSchema],
|
42
42
|
logger: Any, # Changed to Any to support both real and mock loggers
|
43
43
|
) -> IRSchema:
|
44
44
|
"""Constructs the IRSchema object, performs final adjustments, and updates context.
|
@@ -71,10 +71,10 @@ def _finalize_schema_object(
|
|
71
71
|
# Early return of cycle placeholder
|
72
72
|
return existing_schema
|
73
73
|
|
74
|
-
final_enum_values:
|
74
|
+
final_enum_values: List[Any] | None = enum_node if isinstance(enum_node, list) else None
|
75
75
|
final_required_fields_list: List[str] = sorted(list(merged_required_set))
|
76
76
|
|
77
|
-
final_additional_properties:
|
77
|
+
final_additional_properties: Union[bool, IRSchema] | None = None
|
78
78
|
if isinstance(additional_properties_node, bool):
|
79
79
|
final_additional_properties = additional_properties_node
|
80
80
|
elif isinstance(additional_properties_node, dict):
|
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
import logging
|
8
8
|
import os
|
9
|
-
from typing import Any, Callable,
|
9
|
+
from typing import Any, Callable, List, Mapping, Set, Tuple
|
10
10
|
|
11
11
|
from pyopenapi_gen import IRSchema
|
12
12
|
from pyopenapi_gen.core.utils import NameSanitizer
|
@@ -32,9 +32,9 @@ except ValueError:
|
|
32
32
|
|
33
33
|
def _resolve_ref(
|
34
34
|
ref_path_str: str,
|
35
|
-
parent_schema_name:
|
35
|
+
parent_schema_name: str | None, # Name of the schema containing this $ref
|
36
36
|
context: ParsingContext,
|
37
|
-
max_depth_override:
|
37
|
+
max_depth_override: int | None, # Propagated from the main _parse_schema call
|
38
38
|
allow_self_reference_for_parent: bool,
|
39
39
|
) -> IRSchema:
|
40
40
|
"""Resolves a $ref string, handling cycles and depth for the referenced schema."""
|
@@ -82,13 +82,11 @@ def _resolve_ref(
|
|
82
82
|
|
83
83
|
def _parse_composition_keywords(
|
84
84
|
node: Mapping[str, Any],
|
85
|
-
name:
|
85
|
+
name: str | None,
|
86
86
|
context: ParsingContext,
|
87
87
|
max_depth: int,
|
88
|
-
parse_fn: Callable[[
|
89
|
-
) -> Tuple[
|
90
|
-
Optional[List[IRSchema]], Optional[List[IRSchema]], Optional[List[IRSchema]], Dict[str, IRSchema], Set[str], bool
|
91
|
-
]:
|
88
|
+
parse_fn: Callable[[str | None, Mapping[str, Any] | None, ParsingContext, int | None], IRSchema],
|
89
|
+
) -> Tuple[List[IRSchema] | None, List[IRSchema] | None, List[IRSchema] | None, dict[str, IRSchema], Set[str], bool]:
|
92
90
|
"""Parse composition keywords (anyOf, oneOf, allOf) from a schema node.
|
93
91
|
|
94
92
|
Contracts:
|
@@ -101,10 +99,10 @@ def _parse_composition_keywords(
|
|
101
99
|
- Returns a tuple of (any_of_schemas, one_of_schemas, all_of_components,
|
102
100
|
properties, required_fields, is_nullable)
|
103
101
|
"""
|
104
|
-
any_of_schemas:
|
105
|
-
one_of_schemas:
|
106
|
-
parsed_all_of_components:
|
107
|
-
merged_properties:
|
102
|
+
any_of_schemas: List[IRSchema] | None = None
|
103
|
+
one_of_schemas: List[IRSchema] | None = None
|
104
|
+
parsed_all_of_components: List[IRSchema] | None = None
|
105
|
+
merged_properties: dict[str, IRSchema] = {}
|
108
106
|
merged_required_set: Set[str] = set()
|
109
107
|
is_nullable: bool = False
|
110
108
|
|
@@ -130,14 +128,14 @@ def _parse_composition_keywords(
|
|
130
128
|
|
131
129
|
def _parse_properties(
|
132
130
|
properties_node: Mapping[str, Any],
|
133
|
-
parent_schema_name:
|
134
|
-
existing_properties:
|
131
|
+
parent_schema_name: str | None,
|
132
|
+
existing_properties: dict[str, IRSchema], # Properties already merged, e.g., from allOf
|
135
133
|
context: ParsingContext,
|
136
|
-
max_depth_override:
|
134
|
+
max_depth_override: int | None,
|
137
135
|
allow_self_reference: bool,
|
138
|
-
) ->
|
136
|
+
) -> dict[str, IRSchema]:
|
139
137
|
"""Parses the 'properties' block of a schema node."""
|
140
|
-
parsed_props:
|
138
|
+
parsed_props: dict[str, IRSchema] = existing_properties.copy()
|
141
139
|
|
142
140
|
for prop_name, prop_schema_node in properties_node.items():
|
143
141
|
if not isinstance(prop_name, str) or not prop_name:
|
@@ -316,10 +314,10 @@ def _parse_properties(
|
|
316
314
|
|
317
315
|
|
318
316
|
def _parse_schema(
|
319
|
-
schema_name:
|
320
|
-
schema_node:
|
317
|
+
schema_name: str | None,
|
318
|
+
schema_node: Mapping[str, Any] | None,
|
321
319
|
context: ParsingContext,
|
322
|
-
max_depth_override:
|
320
|
+
max_depth_override: int | None = None,
|
323
321
|
allow_self_reference: bool = False,
|
324
322
|
) -> IRSchema:
|
325
323
|
"""
|
@@ -376,6 +374,8 @@ def _parse_schema(
|
|
376
374
|
|
377
375
|
try: # Ensure exit_schema is called
|
378
376
|
if schema_node is None:
|
377
|
+
# Create empty schema for null schema nodes
|
378
|
+
# Do NOT set generation_name - null schemas should resolve to Any inline, not generate separate files
|
379
379
|
return IRSchema(name=NameSanitizer.sanitize_class_name(schema_name) if schema_name else None)
|
380
380
|
|
381
381
|
if not isinstance(schema_node, Mapping):
|
@@ -412,7 +412,7 @@ def _parse_schema(
|
|
412
412
|
|
413
413
|
return resolved_schema
|
414
414
|
|
415
|
-
extracted_type:
|
415
|
+
extracted_type: str | None = None
|
416
416
|
is_nullable_from_type_field = False
|
417
417
|
raw_type_field = schema_node.get("type")
|
418
418
|
|
@@ -455,8 +455,10 @@ def _parse_schema(
|
|
455
455
|
)
|
456
456
|
)
|
457
457
|
|
458
|
-
|
459
|
-
|
458
|
+
# Check for direct nullable field (OpenAPI 3.0 Swagger extension)
|
459
|
+
is_nullable_from_node = schema_node.get("nullable", False)
|
460
|
+
is_nullable_overall = is_nullable_from_type_field or nullable_from_comp or is_nullable_from_node
|
461
|
+
final_properties_for_ir: dict[str, IRSchema] = {}
|
460
462
|
current_final_type = extracted_type
|
461
463
|
if not current_final_type:
|
462
464
|
if props_from_comp or "allOf" in schema_node or "properties" in schema_node:
|
@@ -512,7 +514,7 @@ def _parse_schema(
|
|
512
514
|
if "required" in schema_node and isinstance(schema_node["required"], list):
|
513
515
|
final_required_fields_set.update(schema_node["required"])
|
514
516
|
|
515
|
-
items_ir:
|
517
|
+
items_ir: IRSchema | None = None
|
516
518
|
if current_final_type == "array":
|
517
519
|
items_node = schema_node.get("items")
|
518
520
|
if items_node:
|
@@ -560,6 +562,22 @@ def _parse_schema(
|
|
560
562
|
|
561
563
|
schema_ir_name_attr = NameSanitizer.sanitize_class_name(schema_name) if schema_name else None
|
562
564
|
|
565
|
+
# Parse additionalProperties field
|
566
|
+
additional_properties_value: bool | IRSchema | None = None
|
567
|
+
if "additionalProperties" in schema_node:
|
568
|
+
additional_props_node = schema_node["additionalProperties"]
|
569
|
+
if isinstance(additional_props_node, bool):
|
570
|
+
additional_properties_value = additional_props_node
|
571
|
+
elif isinstance(additional_props_node, dict):
|
572
|
+
# Parse the additionalProperties schema
|
573
|
+
additional_properties_value = _parse_schema(
|
574
|
+
None, # No name for additional properties schema
|
575
|
+
additional_props_node,
|
576
|
+
context,
|
577
|
+
max_depth_override,
|
578
|
+
allow_self_reference,
|
579
|
+
)
|
580
|
+
|
563
581
|
schema_ir = IRSchema(
|
564
582
|
name=schema_ir_name_attr,
|
565
583
|
type=current_final_type,
|
@@ -575,11 +593,12 @@ def _parse_schema(
|
|
575
593
|
example=schema_node.get("example"),
|
576
594
|
is_nullable=is_nullable_overall,
|
577
595
|
items=items_ir,
|
596
|
+
additional_properties=additional_properties_value,
|
578
597
|
)
|
579
598
|
|
580
599
|
if schema_ir.type == "array" and isinstance(schema_node.get("items"), Mapping):
|
581
600
|
raw_items_node = schema_node["items"]
|
582
|
-
item_schema_context_name_for_reparse:
|
601
|
+
item_schema_context_name_for_reparse: str | None
|
583
602
|
base_name_for_reparse_item = schema_name or "AnonymousArray"
|
584
603
|
item_schema_context_name_for_reparse = NameSanitizer.sanitize_class_name(
|
585
604
|
f"{base_name_for_reparse_item}Item"
|
@@ -5,7 +5,7 @@ Handles the extraction of inline enums from schema definitions.
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from typing import Any, Mapping
|
8
|
+
from typing import Any, Mapping
|
9
9
|
|
10
10
|
from .... import IRSchema
|
11
11
|
from ...utils import NameSanitizer
|
@@ -13,12 +13,12 @@ from ..context import ParsingContext
|
|
13
13
|
|
14
14
|
|
15
15
|
def _extract_enum_from_property_node(
|
16
|
-
parent_schema_name:
|
16
|
+
parent_schema_name: str | None,
|
17
17
|
property_key: str,
|
18
18
|
property_node_data: Mapping[str, Any],
|
19
19
|
context: ParsingContext,
|
20
20
|
logger: logging.Logger,
|
21
|
-
) ->
|
21
|
+
) -> IRSchema | None:
|
22
22
|
"""
|
23
23
|
Checks a property's schema node for an inline enum definition.
|
24
24
|
|
@@ -126,7 +126,7 @@ def _extract_enum_from_property_node(
|
|
126
126
|
|
127
127
|
|
128
128
|
def _process_standalone_inline_enum(
|
129
|
-
schema_name:
|
129
|
+
schema_name: str | None, # The original intended name for this schema
|
130
130
|
node_data: Mapping[str, Any], # The raw node data for this schema
|
131
131
|
schema_obj: IRSchema, # The IRSchema object already partially parsed for this node
|
132
132
|
context: ParsingContext,
|
@@ -5,7 +5,10 @@ Handles the promotion of inline object schemas to global schemas.
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
7
|
import logging
|
8
|
-
from typing import
|
8
|
+
from typing import TYPE_CHECKING
|
9
|
+
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
pass
|
9
12
|
|
10
13
|
from .... import IRSchema
|
11
14
|
from ...utils import NameSanitizer
|
@@ -13,12 +16,12 @@ from ..context import ParsingContext
|
|
13
16
|
|
14
17
|
|
15
18
|
def _attempt_promote_inline_object(
|
16
|
-
parent_schema_name:
|
19
|
+
parent_schema_name: str | None, # Name of the schema containing the property
|
17
20
|
property_key: str, # The key (name) of the property being processed
|
18
21
|
property_schema_obj: IRSchema, # The IRSchema of the property itself (already parsed)
|
19
22
|
context: ParsingContext,
|
20
23
|
logger: logging.Logger,
|
21
|
-
) ->
|
24
|
+
) -> IRSchema | None:
|
22
25
|
logger.debug(
|
23
26
|
f"PROMO_ATTEMPT: parent='{parent_schema_name}', prop_key='{property_key}', "
|
24
27
|
f"prop_schema_name='{property_schema_obj.name}', prop_schema_type='{property_schema_obj.type}', "
|
@@ -75,7 +78,7 @@ def _attempt_promote_inline_object(
|
|
75
78
|
else:
|
76
79
|
base_name_candidate = sanitized_prop_key_class_name
|
77
80
|
|
78
|
-
chosen_global_name:
|
81
|
+
chosen_global_name: str | None = None
|
79
82
|
|
80
83
|
# Check if the primary candidate name is available or already points to this object
|
81
84
|
if base_name_candidate in context.parsed_schemas:
|