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.
Files changed (122) hide show
  1. pyopenapi_gen/__init__.py +114 -0
  2. pyopenapi_gen/__main__.py +6 -0
  3. pyopenapi_gen/cli.py +86 -0
  4. pyopenapi_gen/context/file_manager.py +52 -0
  5. pyopenapi_gen/context/import_collector.py +382 -0
  6. pyopenapi_gen/context/render_context.py +630 -0
  7. pyopenapi_gen/core/__init__.py +0 -0
  8. pyopenapi_gen/core/auth/base.py +22 -0
  9. pyopenapi_gen/core/auth/plugins.py +89 -0
  10. pyopenapi_gen/core/exceptions.py +25 -0
  11. pyopenapi_gen/core/http_transport.py +219 -0
  12. pyopenapi_gen/core/loader/__init__.py +12 -0
  13. pyopenapi_gen/core/loader/loader.py +158 -0
  14. pyopenapi_gen/core/loader/operations/__init__.py +12 -0
  15. pyopenapi_gen/core/loader/operations/parser.py +155 -0
  16. pyopenapi_gen/core/loader/operations/post_processor.py +60 -0
  17. pyopenapi_gen/core/loader/operations/request_body.py +85 -0
  18. pyopenapi_gen/core/loader/parameters/__init__.py +10 -0
  19. pyopenapi_gen/core/loader/parameters/parser.py +121 -0
  20. pyopenapi_gen/core/loader/responses/__init__.py +10 -0
  21. pyopenapi_gen/core/loader/responses/parser.py +104 -0
  22. pyopenapi_gen/core/loader/schemas/__init__.py +11 -0
  23. pyopenapi_gen/core/loader/schemas/extractor.py +184 -0
  24. pyopenapi_gen/core/pagination.py +64 -0
  25. pyopenapi_gen/core/parsing/__init__.py +13 -0
  26. pyopenapi_gen/core/parsing/common/__init__.py +1 -0
  27. pyopenapi_gen/core/parsing/common/ref_resolution/__init__.py +9 -0
  28. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/__init__.py +0 -0
  29. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/cyclic_properties.py +66 -0
  30. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/direct_cycle.py +33 -0
  31. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/existing_schema.py +22 -0
  32. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/list_response.py +54 -0
  33. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/missing_ref.py +52 -0
  34. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/new_schema.py +50 -0
  35. pyopenapi_gen/core/parsing/common/ref_resolution/helpers/stripped_suffix.py +51 -0
  36. pyopenapi_gen/core/parsing/common/ref_resolution/resolve_schema_ref.py +86 -0
  37. pyopenapi_gen/core/parsing/common/type_parser.py +74 -0
  38. pyopenapi_gen/core/parsing/context.py +184 -0
  39. pyopenapi_gen/core/parsing/cycle_helpers.py +123 -0
  40. pyopenapi_gen/core/parsing/keywords/__init__.py +1 -0
  41. pyopenapi_gen/core/parsing/keywords/all_of_parser.py +77 -0
  42. pyopenapi_gen/core/parsing/keywords/any_of_parser.py +79 -0
  43. pyopenapi_gen/core/parsing/keywords/array_items_parser.py +69 -0
  44. pyopenapi_gen/core/parsing/keywords/one_of_parser.py +72 -0
  45. pyopenapi_gen/core/parsing/keywords/properties_parser.py +98 -0
  46. pyopenapi_gen/core/parsing/schema_finalizer.py +166 -0
  47. pyopenapi_gen/core/parsing/schema_parser.py +610 -0
  48. pyopenapi_gen/core/parsing/transformers/__init__.py +0 -0
  49. pyopenapi_gen/core/parsing/transformers/inline_enum_extractor.py +285 -0
  50. pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py +117 -0
  51. pyopenapi_gen/core/parsing/unified_cycle_detection.py +293 -0
  52. pyopenapi_gen/core/postprocess_manager.py +161 -0
  53. pyopenapi_gen/core/schemas.py +40 -0
  54. pyopenapi_gen/core/streaming_helpers.py +86 -0
  55. pyopenapi_gen/core/telemetry.py +67 -0
  56. pyopenapi_gen/core/utils.py +409 -0
  57. pyopenapi_gen/core/warning_collector.py +83 -0
  58. pyopenapi_gen/core/writers/code_writer.py +135 -0
  59. pyopenapi_gen/core/writers/documentation_writer.py +222 -0
  60. pyopenapi_gen/core/writers/line_writer.py +217 -0
  61. pyopenapi_gen/core/writers/python_construct_renderer.py +274 -0
  62. pyopenapi_gen/core_package_template/README.md +21 -0
  63. pyopenapi_gen/emit/models_emitter.py +143 -0
  64. pyopenapi_gen/emitters/client_emitter.py +51 -0
  65. pyopenapi_gen/emitters/core_emitter.py +181 -0
  66. pyopenapi_gen/emitters/docs_emitter.py +44 -0
  67. pyopenapi_gen/emitters/endpoints_emitter.py +223 -0
  68. pyopenapi_gen/emitters/exceptions_emitter.py +52 -0
  69. pyopenapi_gen/emitters/models_emitter.py +428 -0
  70. pyopenapi_gen/generator/client_generator.py +562 -0
  71. pyopenapi_gen/helpers/__init__.py +1 -0
  72. pyopenapi_gen/helpers/endpoint_utils.py +552 -0
  73. pyopenapi_gen/helpers/type_cleaner.py +341 -0
  74. pyopenapi_gen/helpers/type_helper.py +112 -0
  75. pyopenapi_gen/helpers/type_resolution/__init__.py +1 -0
  76. pyopenapi_gen/helpers/type_resolution/array_resolver.py +57 -0
  77. pyopenapi_gen/helpers/type_resolution/composition_resolver.py +79 -0
  78. pyopenapi_gen/helpers/type_resolution/finalizer.py +89 -0
  79. pyopenapi_gen/helpers/type_resolution/named_resolver.py +174 -0
  80. pyopenapi_gen/helpers/type_resolution/object_resolver.py +212 -0
  81. pyopenapi_gen/helpers/type_resolution/primitive_resolver.py +57 -0
  82. pyopenapi_gen/helpers/type_resolution/resolver.py +48 -0
  83. pyopenapi_gen/helpers/url_utils.py +14 -0
  84. pyopenapi_gen/http_types.py +20 -0
  85. pyopenapi_gen/ir.py +167 -0
  86. pyopenapi_gen/py.typed +1 -0
  87. pyopenapi_gen/types/__init__.py +11 -0
  88. pyopenapi_gen/types/contracts/__init__.py +13 -0
  89. pyopenapi_gen/types/contracts/protocols.py +106 -0
  90. pyopenapi_gen/types/contracts/types.py +30 -0
  91. pyopenapi_gen/types/resolvers/__init__.py +7 -0
  92. pyopenapi_gen/types/resolvers/reference_resolver.py +71 -0
  93. pyopenapi_gen/types/resolvers/response_resolver.py +203 -0
  94. pyopenapi_gen/types/resolvers/schema_resolver.py +367 -0
  95. pyopenapi_gen/types/services/__init__.py +5 -0
  96. pyopenapi_gen/types/services/type_service.py +133 -0
  97. pyopenapi_gen/visit/client_visitor.py +228 -0
  98. pyopenapi_gen/visit/docs_visitor.py +38 -0
  99. pyopenapi_gen/visit/endpoint/__init__.py +1 -0
  100. pyopenapi_gen/visit/endpoint/endpoint_visitor.py +103 -0
  101. pyopenapi_gen/visit/endpoint/generators/__init__.py +1 -0
  102. pyopenapi_gen/visit/endpoint/generators/docstring_generator.py +121 -0
  103. pyopenapi_gen/visit/endpoint/generators/endpoint_method_generator.py +87 -0
  104. pyopenapi_gen/visit/endpoint/generators/request_generator.py +103 -0
  105. pyopenapi_gen/visit/endpoint/generators/response_handler_generator.py +497 -0
  106. pyopenapi_gen/visit/endpoint/generators/signature_generator.py +88 -0
  107. pyopenapi_gen/visit/endpoint/generators/url_args_generator.py +183 -0
  108. pyopenapi_gen/visit/endpoint/processors/__init__.py +1 -0
  109. pyopenapi_gen/visit/endpoint/processors/import_analyzer.py +76 -0
  110. pyopenapi_gen/visit/endpoint/processors/parameter_processor.py +171 -0
  111. pyopenapi_gen/visit/exception_visitor.py +52 -0
  112. pyopenapi_gen/visit/model/__init__.py +0 -0
  113. pyopenapi_gen/visit/model/alias_generator.py +89 -0
  114. pyopenapi_gen/visit/model/dataclass_generator.py +197 -0
  115. pyopenapi_gen/visit/model/enum_generator.py +200 -0
  116. pyopenapi_gen/visit/model/model_visitor.py +197 -0
  117. pyopenapi_gen/visit/visitor.py +97 -0
  118. pyopenapi_gen-0.8.3.dist-info/METADATA +224 -0
  119. pyopenapi_gen-0.8.3.dist-info/RECORD +122 -0
  120. pyopenapi_gen-0.8.3.dist-info/WHEEL +4 -0
  121. pyopenapi_gen-0.8.3.dist-info/entry_points.txt +2 -0
  122. 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