pyopenapi-gen 0.8.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyopenapi_gen/__init__.py +114 -0
- pyopenapi_gen/__main__.py +6 -0
- pyopenapi_gen/cli.py +86 -0
- pyopenapi_gen/context/file_manager.py +52 -0
- pyopenapi_gen/context/import_collector.py +382 -0
- pyopenapi_gen/context/render_context.py +630 -0
- pyopenapi_gen/core/__init__.py +0 -0
- pyopenapi_gen/core/auth/base.py +22 -0
- pyopenapi_gen/core/auth/plugins.py +89 -0
- pyopenapi_gen/core/exceptions.py +25 -0
- pyopenapi_gen/core/http_transport.py +219 -0
- pyopenapi_gen/core/loader/__init__.py +12 -0
- pyopenapi_gen/core/loader/loader.py +158 -0
- pyopenapi_gen/core/loader/operations/__init__.py +12 -0
- pyopenapi_gen/core/loader/operations/parser.py +155 -0
- pyopenapi_gen/core/loader/operations/post_processor.py +60 -0
- pyopenapi_gen/core/loader/operations/request_body.py +85 -0
- pyopenapi_gen/core/loader/parameters/__init__.py +10 -0
- pyopenapi_gen/core/loader/parameters/parser.py +121 -0
- pyopenapi_gen/core/loader/responses/__init__.py +10 -0
- pyopenapi_gen/core/loader/responses/parser.py +104 -0
- pyopenapi_gen/core/loader/schemas/__init__.py +11 -0
- pyopenapi_gen/core/loader/schemas/extractor.py +184 -0
- pyopenapi_gen/core/pagination.py +64 -0
- pyopenapi_gen/core/parsing/__init__.py +13 -0
- pyopenapi_gen/core/parsing/common/__init__.py +1 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/__init__.py +9 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/__init__.py +0 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/cyclic_properties.py +66 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/direct_cycle.py +33 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/existing_schema.py +22 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/list_response.py +54 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py +52 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py +50 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py +51 -0
- pyopenapi_gen/core/parsing/common/ref_resolution/resolve_schema_ref.py +86 -0
- pyopenapi_gen/core/parsing/common/type_parser.py +74 -0
- pyopenapi_gen/core/parsing/context.py +184 -0
- pyopenapi_gen/core/parsing/cycle_helpers.py +123 -0
- pyopenapi_gen/core/parsing/keywords/__init__.py +1 -0
- pyopenapi_gen/core/parsing/keywords/all_of_parser.py +77 -0
- pyopenapi_gen/core/parsing/keywords/any_of_parser.py +79 -0
- pyopenapi_gen/core/parsing/keywords/array_items_parser.py +69 -0
- pyopenapi_gen/core/parsing/keywords/one_of_parser.py +72 -0
- pyopenapi_gen/core/parsing/keywords/properties_parser.py +98 -0
- pyopenapi_gen/core/parsing/schema_finalizer.py +166 -0
- pyopenapi_gen/core/parsing/schema_parser.py +610 -0
- pyopenapi_gen/core/parsing/transformers/__init__.py +0 -0
- pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +285 -0
- pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py +117 -0
- pyopenapi_gen/core/parsing/unified_cycle_detection.py +293 -0
- pyopenapi_gen/core/postprocess_manager.py +161 -0
- pyopenapi_gen/core/schemas.py +40 -0
- pyopenapi_gen/core/streaming_helpers.py +86 -0
- pyopenapi_gen/core/telemetry.py +67 -0
- pyopenapi_gen/core/utils.py +409 -0
- pyopenapi_gen/core/warning_collector.py +83 -0
- pyopenapi_gen/core/writers/code_writer.py +135 -0
- pyopenapi_gen/core/writers/documentation_writer.py +222 -0
- pyopenapi_gen/core/writers/line_writer.py +217 -0
- pyopenapi_gen/core/writers/python_construct_renderer.py +274 -0
- pyopenapi_gen/core_package_template/README.md +21 -0
- pyopenapi_gen/emit/models_emitter.py +143 -0
- pyopenapi_gen/emitters/client_emitter.py +51 -0
- pyopenapi_gen/emitters/core_emitter.py +181 -0
- pyopenapi_gen/emitters/docs_emitter.py +44 -0
- pyopenapi_gen/emitters/endpoints_emitter.py +223 -0
- pyopenapi_gen/emitters/exceptions_emitter.py +52 -0
- pyopenapi_gen/emitters/models_emitter.py +428 -0
- pyopenapi_gen/generator/client_generator.py +562 -0
- pyopenapi_gen/helpers/__init__.py +1 -0
- pyopenapi_gen/helpers/endpoint_utils.py +552 -0
- pyopenapi_gen/helpers/type_cleaner.py +341 -0
- pyopenapi_gen/helpers/type_helper.py +112 -0
- pyopenapi_gen/helpers/type_resolution/__init__.py +1 -0
- pyopenapi_gen/helpers/type_resolution/array_resolver.py +57 -0
- pyopenapi_gen/helpers/type_resolution/composition_resolver.py +79 -0
- pyopenapi_gen/helpers/type_resolution/finalizer.py +89 -0
- pyopenapi_gen/helpers/type_resolution/named_resolver.py +174 -0
- pyopenapi_gen/helpers/type_resolution/object_resolver.py +212 -0
- pyopenapi_gen/helpers/type_resolution/primitive_resolver.py +57 -0
- pyopenapi_gen/helpers/type_resolution/resolver.py +48 -0
- pyopenapi_gen/helpers/url_utils.py +14 -0
- pyopenapi_gen/http_types.py +20 -0
- pyopenapi_gen/ir.py +167 -0
- pyopenapi_gen/py.typed +1 -0
- pyopenapi_gen/types/__init__.py +11 -0
- pyopenapi_gen/types/contracts/__init__.py +13 -0
- pyopenapi_gen/types/contracts/protocols.py +106 -0
- pyopenapi_gen/types/contracts/types.py +30 -0
- pyopenapi_gen/types/resolvers/__init__.py +7 -0
- pyopenapi_gen/types/resolvers/reference_resolver.py +71 -0
- pyopenapi_gen/types/resolvers/response_resolver.py +203 -0
- pyopenapi_gen/types/resolvers/schema_resolver.py +367 -0
- pyopenapi_gen/types/services/__init__.py +5 -0
- pyopenapi_gen/types/services/type_service.py +133 -0
- pyopenapi_gen/visit/client_visitor.py +228 -0
- pyopenapi_gen/visit/docs_visitor.py +38 -0
- pyopenapi_gen/visit/endpoint/__init__.py +1 -0
- pyopenapi_gen/visit/endpoint/endpoint_visitor.py +103 -0
- pyopenapi_gen/visit/endpoint/generators/__init__.py +1 -0
- pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +121 -0
- pyopenapi_gen/visit/endpoint/generators/endpoint_method_generator.py +87 -0
- pyopenapi_gen/visit/endpoint/generators/request_generator.py +103 -0
- pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +497 -0
- pyopenapi_gen/visit/endpoint/generators/signature_generator.py +88 -0
- pyopenapi_gen/visit/endpoint/generators/url_args_generator.py +183 -0
- pyopenapi_gen/visit/endpoint/processors/__init__.py +1 -0
- pyopenapi_gen/visit/endpoint/processors/import_analyzer.py +76 -0
- pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +171 -0
- pyopenapi_gen/visit/exception_visitor.py +52 -0
- pyopenapi_gen/visit/model/__init__.py +0 -0
- pyopenapi_gen/visit/model/alias_generator.py +89 -0
- pyopenapi_gen/visit/model/dataclass_generator.py +197 -0
- pyopenapi_gen/visit/model/enum_generator.py +200 -0
- pyopenapi_gen/visit/model/model_visitor.py +197 -0
- pyopenapi_gen/visit/visitor.py +97 -0
- pyopenapi_gen-0.8.3.dist-info/METADATA +224 -0
- pyopenapi_gen-0.8.3.dist-info/RECORD +122 -0
- pyopenapi_gen-0.8.3.dist-info/WHEEL +4 -0
- pyopenapi_gen-0.8.3.dist-info/entry_points.txt +2 -0
- pyopenapi_gen-0.8.3.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping, Optional
|
5
|
+
|
6
|
+
# Import NameSanitizer for use in name generation
|
7
|
+
from pyopenapi_gen.core.utils import NameSanitizer
|
8
|
+
|
9
|
+
# REVERTED: TEMPORARY DEBUG LOGGER REMOVED
|
10
|
+
# properties_logger = logging.getLogger("pyopenapi_gen.core.parsing.keywords.properties_parser_DEBUG")
|
11
|
+
# properties_logger.setLevel(logging.DEBUG)
|
12
|
+
# # Ensure there's a handler if running in some environments
|
13
|
+
# if not properties_logger.handlers:
|
14
|
+
# properties_logger.addHandler(logging.StreamHandler())
|
15
|
+
from ..context import ParsingContext
|
16
|
+
from ..transformers.inline_object_promoter import _attempt_promote_inline_object
|
17
|
+
|
18
|
+
# Specific helpers needed by _parse_properties
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from pyopenapi_gen import IRSchema # Main IR model
|
22
|
+
|
23
|
+
# from ..context import ParsingContext # No longer here
|
24
|
+
# No direct import of _parse_schema from schema_parser to avoid circularity
|
25
|
+
pass
|
26
|
+
|
27
|
+
|
28
|
+
def _parse_properties(
|
29
|
+
properties_node: Mapping[str, Any],
|
30
|
+
parent_schema_name: Optional[str],
|
31
|
+
context: ParsingContext,
|
32
|
+
max_depth: int,
|
33
|
+
parse_fn: Callable[[Optional[str], Optional[Mapping[str, Any]], ParsingContext, Optional[int]], IRSchema],
|
34
|
+
logger: logging.Logger,
|
35
|
+
) -> Dict[str, IRSchema]:
|
36
|
+
"""Parse properties from a schema node.
|
37
|
+
|
38
|
+
Contracts:
|
39
|
+
Pre-conditions:
|
40
|
+
- properties_node is a valid Mapping
|
41
|
+
- context is a valid ParsingContext instance
|
42
|
+
- max_depth >= 0
|
43
|
+
- parse_fn is a callable for parsing schemas
|
44
|
+
Post-conditions:
|
45
|
+
- Returns a dictionary mapping property names to IRSchema instances
|
46
|
+
- Property references are properly maintained
|
47
|
+
"""
|
48
|
+
properties_map: Dict[str, IRSchema] = {}
|
49
|
+
|
50
|
+
for prop_key, prop_schema_node in properties_node.items():
|
51
|
+
# Skip invalid property names
|
52
|
+
if not prop_key or not isinstance(prop_key, str):
|
53
|
+
logger.warning(f"Skipping property with invalid name: {prop_key}")
|
54
|
+
continue
|
55
|
+
|
56
|
+
# Align with _parse_schema property naming logic:
|
57
|
+
# ParentNamePropName or just PropName if parent is anonymous.
|
58
|
+
sanitized_prop_key_for_name = NameSanitizer.sanitize_class_name(prop_key)
|
59
|
+
if parent_schema_name:
|
60
|
+
# Ensure parent_schema_name is also sanitized if it's part of the name
|
61
|
+
# This is implicitly handled if parent_schema_name comes from an IRSchema.name already.
|
62
|
+
# For direct use, ensure it's PascalCase for consistency,
|
63
|
+
# though _parse_schema currently doesn't re-sanitize it.
|
64
|
+
# Assuming parent_schema_name is already a valid schema name (e.g. "ParentSchema")
|
65
|
+
prop_schema_context_name = f"{parent_schema_name}{sanitized_prop_key_for_name}"
|
66
|
+
else:
|
67
|
+
prop_schema_context_name = sanitized_prop_key_for_name
|
68
|
+
|
69
|
+
# Parse the property schema
|
70
|
+
prop_schema_ir = parse_fn(prop_schema_context_name, prop_schema_node, context, max_depth)
|
71
|
+
|
72
|
+
# Handle $ref in property schema
|
73
|
+
if isinstance(prop_schema_node, Mapping) and "$ref" in prop_schema_node:
|
74
|
+
ref_value = prop_schema_node["$ref"]
|
75
|
+
ref_name = ref_value.split("/")[-1]
|
76
|
+
if ref_name in context.parsed_schemas:
|
77
|
+
prop_schema_ir._refers_to_schema = context.parsed_schemas[ref_name]
|
78
|
+
|
79
|
+
# Attempt to promote inline object
|
80
|
+
promoted_ir = _attempt_promote_inline_object(parent_schema_name, prop_key, prop_schema_ir, context, logger)
|
81
|
+
if promoted_ir is not None:
|
82
|
+
properties_map[prop_key] = promoted_ir
|
83
|
+
logger.debug(
|
84
|
+
f"Added promoted '{prop_key}' (name: {getattr(promoted_ir, 'name', 'N/A')}) "
|
85
|
+
f"to properties_map for '{parent_schema_name}'"
|
86
|
+
)
|
87
|
+
else:
|
88
|
+
properties_map[prop_key] = prop_schema_ir
|
89
|
+
logger.debug(
|
90
|
+
f"Added original '{prop_key}' (name: {getattr(prop_schema_ir, 'name', 'N/A')}, "
|
91
|
+
f"type: {getattr(prop_schema_ir, 'type', 'N/A')}, "
|
92
|
+
f"circular: {getattr(prop_schema_ir, '_is_circular_ref', 'N/A')}) "
|
93
|
+
f"to properties_map for '{parent_schema_name}'"
|
94
|
+
)
|
95
|
+
|
96
|
+
logger.debug(f"_parse_properties FINALLY returning for parent '{parent_schema_name}': {properties_map}")
|
97
|
+
# import pdb; pdb.set_trace() # For local debugging if needed
|
98
|
+
return properties_map
|
@@ -0,0 +1,166 @@
|
|
1
|
+
"""
|
2
|
+
Finalization an IRSchema object during OpenAPI parsing.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Set, Union
|
8
|
+
|
9
|
+
from pyopenapi_gen import IRSchema
|
10
|
+
from pyopenapi_gen.core.utils import NameSanitizer
|
11
|
+
|
12
|
+
from .context import ParsingContext
|
13
|
+
|
14
|
+
# Specific helpers needed
|
15
|
+
from .transformers.inline_enum_extractor import _process_standalone_inline_enum
|
16
|
+
|
17
|
+
# Note: _parse_schema will be passed as parse_fn, not imported directly
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
def _finalize_schema_object(
|
24
|
+
name: Optional[str],
|
25
|
+
node: Mapping[str, Any],
|
26
|
+
context: ParsingContext,
|
27
|
+
schema_type: Optional[str],
|
28
|
+
is_nullable: bool,
|
29
|
+
any_of_schemas: Optional[List[IRSchema]],
|
30
|
+
one_of_schemas: Optional[List[IRSchema]],
|
31
|
+
parsed_all_of_components: Optional[List[IRSchema]],
|
32
|
+
final_properties_map: Dict[str, IRSchema],
|
33
|
+
merged_required_set: Set[str],
|
34
|
+
final_items_schema: Optional[IRSchema],
|
35
|
+
additional_properties_node: Optional[Union[bool, Mapping[str, Any]]],
|
36
|
+
enum_node: Optional[List[Any]],
|
37
|
+
format_node: Optional[str],
|
38
|
+
description_node: Optional[str],
|
39
|
+
from_unresolved_ref_node: bool,
|
40
|
+
max_depth: int,
|
41
|
+
parse_fn: Callable[[Optional[str], Optional[Mapping[str, Any]], ParsingContext, int], IRSchema],
|
42
|
+
logger: Any, # Changed to Any to support both real and mock loggers
|
43
|
+
) -> IRSchema:
|
44
|
+
"""Constructs the IRSchema object, performs final adjustments, and updates context.
|
45
|
+
|
46
|
+
Contracts:
|
47
|
+
Pre-conditions:
|
48
|
+
- All input arguments are appropriately populated.
|
49
|
+
- context is valid.
|
50
|
+
- parse_fn is a callable for parsing (e.g. additionalProperties).
|
51
|
+
- logger is a logging.Logger instance or a mock logger for testing.
|
52
|
+
Post-conditions:
|
53
|
+
- Returns a finalized IRSchema instance.
|
54
|
+
- If schema has a name and isn't a placeholder, it's in context.parsed_schemas.
|
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"
|
59
|
+
# Remove logger type check to support mock loggers in tests
|
60
|
+
|
61
|
+
# If a placeholder for this schema name already exists due to a cycle detected deeper,
|
62
|
+
# return that placeholder immediately to preserve the cycle information.
|
63
|
+
if name and name in context.parsed_schemas:
|
64
|
+
existing_schema = context.parsed_schemas[name]
|
65
|
+
if getattr(existing_schema, "_is_circular_ref", False) or getattr(
|
66
|
+
existing_schema, "_from_unresolved_ref", False
|
67
|
+
):
|
68
|
+
# Early return of cycle placeholder
|
69
|
+
return existing_schema
|
70
|
+
|
71
|
+
final_enum_values: Optional[List[Any]] = enum_node if isinstance(enum_node, list) else None
|
72
|
+
final_required_fields_list: List[str] = sorted(list(merged_required_set))
|
73
|
+
|
74
|
+
final_additional_properties: Optional[Union[bool, IRSchema]] = None
|
75
|
+
if isinstance(additional_properties_node, bool):
|
76
|
+
final_additional_properties = additional_properties_node
|
77
|
+
elif isinstance(additional_properties_node, dict):
|
78
|
+
final_additional_properties = parse_fn(None, additional_properties_node, context, max_depth)
|
79
|
+
|
80
|
+
final_schema_name_for_obj = NameSanitizer.sanitize_class_name(name) if name else None
|
81
|
+
current_schema_type = schema_type
|
82
|
+
if current_schema_type is None and final_properties_map:
|
83
|
+
current_schema_type = "object"
|
84
|
+
|
85
|
+
is_data_wrapper_flag = (
|
86
|
+
current_schema_type == "object"
|
87
|
+
and "data" in final_properties_map
|
88
|
+
and "data" in final_required_fields_list
|
89
|
+
and len(final_properties_map) == 1
|
90
|
+
)
|
91
|
+
|
92
|
+
schema_obj = IRSchema(
|
93
|
+
name=final_schema_name_for_obj,
|
94
|
+
type=current_schema_type,
|
95
|
+
format=format_node,
|
96
|
+
description=description_node,
|
97
|
+
required=final_required_fields_list,
|
98
|
+
properties=final_properties_map,
|
99
|
+
items=final_items_schema,
|
100
|
+
enum=final_enum_values,
|
101
|
+
additional_properties=final_additional_properties,
|
102
|
+
is_nullable=is_nullable,
|
103
|
+
any_of=any_of_schemas,
|
104
|
+
one_of=one_of_schemas,
|
105
|
+
all_of=parsed_all_of_components,
|
106
|
+
is_data_wrapper=is_data_wrapper_flag,
|
107
|
+
_from_unresolved_ref=from_unresolved_ref_node,
|
108
|
+
)
|
109
|
+
|
110
|
+
if (
|
111
|
+
schema_obj.name
|
112
|
+
and schema_obj.name in context.parsed_schemas
|
113
|
+
and context.parsed_schemas[schema_obj.name].type is None
|
114
|
+
and schema_obj.type is not None
|
115
|
+
):
|
116
|
+
# Named schema in context has no type, adopting type
|
117
|
+
# schema_obj will be placed in context later, overwriting if necessary.
|
118
|
+
pass
|
119
|
+
|
120
|
+
if schema_obj.type is None and (
|
121
|
+
schema_obj.properties
|
122
|
+
or (
|
123
|
+
isinstance(node.get("_def"), dict)
|
124
|
+
and node["_def"].get("typeName") == "ZodObject"
|
125
|
+
and node["_def"].get("shape")
|
126
|
+
)
|
127
|
+
):
|
128
|
+
# Schema has properties but no type; setting to 'object'
|
129
|
+
schema_obj.type = "object"
|
130
|
+
|
131
|
+
if name and "." in name:
|
132
|
+
is_explicitly_simple = node.get("type") in ["string", "integer", "number", "boolean", "array"]
|
133
|
+
is_explicitly_enum = "enum" in node
|
134
|
+
if schema_obj.type is None and not is_explicitly_simple and not is_explicitly_enum:
|
135
|
+
# Inline property lacks complex type, defaulting to 'object'
|
136
|
+
schema_obj.type = "object"
|
137
|
+
|
138
|
+
# Update the context with the finalized schema object
|
139
|
+
# This should only happen if we didn't return an existing placeholder above.
|
140
|
+
if name:
|
141
|
+
# Never overwrite a cycle placeholder!
|
142
|
+
if name in context.parsed_schemas and (
|
143
|
+
getattr(context.parsed_schemas[name], "_is_circular_ref", False)
|
144
|
+
or getattr(context.parsed_schemas[name], "_from_unresolved_ref", False)
|
145
|
+
):
|
146
|
+
# Not overwriting cycle placeholder
|
147
|
+
pass # Do not overwrite
|
148
|
+
elif name not in context.parsed_schemas:
|
149
|
+
context.parsed_schemas[name] = schema_obj
|
150
|
+
# If name is in context and is already a full schema, and schema_obj is also full,
|
151
|
+
# current one is considered fresher.
|
152
|
+
elif not (
|
153
|
+
getattr(context.parsed_schemas[name], "_is_circular_ref", False)
|
154
|
+
or getattr(context.parsed_schemas[name], "_from_unresolved_ref", False)
|
155
|
+
):
|
156
|
+
context.parsed_schemas[name] = schema_obj # Overwrite existing full with new full
|
157
|
+
|
158
|
+
if schema_obj:
|
159
|
+
schema_obj = _process_standalone_inline_enum(name, node, schema_obj, context, logger)
|
160
|
+
|
161
|
+
if schema_obj and schema_obj.name and context.parsed_schemas.get(schema_obj.name) is not schema_obj:
|
162
|
+
context.parsed_schemas[schema_obj.name] = schema_obj
|
163
|
+
# Ensured schema is in context post-standalone-enum processing
|
164
|
+
|
165
|
+
# Returning finalized schema object
|
166
|
+
return schema_obj
|