pyopenapi-gen 0.14.2__py3-none-any.whl → 0.14.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/context/render_context.py +97 -1
- pyopenapi_gen/core/writers/python_construct_renderer.py +2 -15
- pyopenapi_gen/visit/model/dataclass_generator.py +2 -1
- {pyopenapi_gen-0.14.2.dist-info → pyopenapi_gen-0.14.3.dist-info}/METADATA +1 -1
- {pyopenapi_gen-0.14.2.dist-info → pyopenapi_gen-0.14.3.dist-info}/RECORD +8 -8
- {pyopenapi_gen-0.14.2.dist-info → pyopenapi_gen-0.14.3.dist-info}/WHEEL +0 -0
- {pyopenapi_gen-0.14.2.dist-info → pyopenapi_gen-0.14.3.dist-info}/entry_points.txt +0 -0
- {pyopenapi_gen-0.14.2.dist-info → pyopenapi_gen-0.14.3.dist-info}/licenses/LICENSE +0 -0
@@ -153,10 +153,25 @@ class RenderContext:
|
|
153
153
|
self.core_package_name + "."
|
154
154
|
)
|
155
155
|
if is_target_in_core_pkg_namespace:
|
156
|
+
# For root-level sibling packages (core and output package both at top level),
|
157
|
+
# we MUST use absolute imports because Python doesn't support relative imports
|
158
|
+
# that go beyond the top-level package.
|
159
|
+
#
|
160
|
+
# Example structure that requires absolute imports:
|
161
|
+
# output/ # NOT a package (no __init__.py)
|
162
|
+
# ├── core/ # top-level package
|
163
|
+
# └── businessapi/ # top-level package
|
164
|
+
#
|
165
|
+
# In this case, businessapi/client.py cannot use "from ..core import X"
|
166
|
+
# because that would try to go UP from businessapi to output/, which is not a package.
|
167
|
+
#
|
168
|
+
# Solution: Always use absolute imports for core when it's a root-level sibling.
|
169
|
+
|
170
|
+
# Always use absolute imports for core imports
|
156
171
|
if name:
|
157
172
|
self.import_collector.add_import(module=logical_module, name=name)
|
158
173
|
else:
|
159
|
-
self.import_collector.add_plain_import(module=logical_module)
|
174
|
+
self.import_collector.add_plain_import(module=logical_module)
|
160
175
|
return
|
161
176
|
|
162
177
|
# 3. Stdlib/Builtin?
|
@@ -628,3 +643,84 @@ class RenderContext:
|
|
628
643
|
|
629
644
|
except ValueError: # If current_file is not under overall_project_root
|
630
645
|
return None
|
646
|
+
|
647
|
+
def get_core_import_path(self, submodule: str) -> str:
|
648
|
+
"""
|
649
|
+
Calculate the correct import path to a core package submodule from the current file.
|
650
|
+
|
651
|
+
Handles both sibling core packages (core/, custom_core/) and external packages (api_sdks.my_core).
|
652
|
+
|
653
|
+
Args:
|
654
|
+
submodule: The submodule within core to import from (e.g., "schemas", "http_transport", "exceptions")
|
655
|
+
|
656
|
+
Returns:
|
657
|
+
The correct import path string (e.g., "...core.schemas", "..core.http_transport", "api_sdks.my_core.schemas")
|
658
|
+
|
659
|
+
Examples:
|
660
|
+
From businessapi/models/user.py → core/schemas.py returns "...core.schemas"
|
661
|
+
From businessapi/client.py → core/http_transport.py returns "..core.http_transport"
|
662
|
+
From businessapi/endpoints/auth.py → core/exceptions.py returns "...core.exceptions"
|
663
|
+
External core package (api_sdks.my_core) returns "api_sdks.my_core.schemas"
|
664
|
+
"""
|
665
|
+
# 1. Check if core_package_name contains dots or is already a relative import
|
666
|
+
if "." in self.core_package_name and not self.core_package_name.startswith("."):
|
667
|
+
# External package (e.g., "api_sdks.my_core") - use absolute import
|
668
|
+
return f"{self.core_package_name}.{submodule}"
|
669
|
+
|
670
|
+
if self.core_package_name.startswith(".."):
|
671
|
+
# Already a relative import path
|
672
|
+
return f"{self.core_package_name}.{submodule}"
|
673
|
+
|
674
|
+
# 2. Local core package (sibling) - calculate relative path
|
675
|
+
return self._calculate_relative_core_path(submodule)
|
676
|
+
|
677
|
+
def _calculate_relative_core_path(self, submodule: str) -> str:
|
678
|
+
"""Calculate relative import path to sibling core package."""
|
679
|
+
|
680
|
+
if not self.current_file or not self.package_root_for_generated_code or not self.overall_project_root:
|
681
|
+
# Fallback to absolute import if context not fully set
|
682
|
+
logger.warning(
|
683
|
+
f"Cannot calculate relative core path: context not fully set. "
|
684
|
+
f"current_file={self.current_file}, package_root={self.package_root_for_generated_code}, "
|
685
|
+
f"project_root={self.overall_project_root}. Using absolute import."
|
686
|
+
)
|
687
|
+
return f"{self.core_package_name}.{submodule}"
|
688
|
+
|
689
|
+
try:
|
690
|
+
# 1. Get current file's directory
|
691
|
+
current_file_abs = Path(self.current_file).resolve()
|
692
|
+
current_dir_abs = current_file_abs.parent
|
693
|
+
|
694
|
+
# 2. Determine core package location (sibling to output package)
|
695
|
+
project_root_abs = Path(self.overall_project_root).resolve()
|
696
|
+
|
697
|
+
# Core is sibling to the output package
|
698
|
+
core_abs = project_root_abs / self.core_package_name
|
699
|
+
target_abs = core_abs / submodule.replace(".", os.sep)
|
700
|
+
|
701
|
+
# 3. Calculate relative path from current directory to target
|
702
|
+
relative_path = os.path.relpath(target_abs, start=current_dir_abs)
|
703
|
+
|
704
|
+
# 4. Convert filesystem path to Python import path
|
705
|
+
# e.g., "../../core/schemas" → "...core.schemas"
|
706
|
+
path_parts = relative_path.split(os.sep)
|
707
|
+
|
708
|
+
dots = 0
|
709
|
+
module_parts = []
|
710
|
+
|
711
|
+
for part in path_parts:
|
712
|
+
if part == "..":
|
713
|
+
dots += 1
|
714
|
+
elif part != ".":
|
715
|
+
module_parts.append(part)
|
716
|
+
|
717
|
+
# Prefix with dots (add one more for Python relative imports)
|
718
|
+
prefix = "." * (dots + 1)
|
719
|
+
module_path = ".".join(module_parts)
|
720
|
+
|
721
|
+
return f"{prefix}{module_path}"
|
722
|
+
|
723
|
+
except Exception as e:
|
724
|
+
# Fallback to absolute import on any error
|
725
|
+
logger.warning(f"Failed to calculate relative core path: {e}. Using absolute import.")
|
726
|
+
return f"{self.core_package_name}.{submodule}"
|
@@ -183,21 +183,8 @@ class PythonConstructRenderer:
|
|
183
183
|
context.add_import("dataclasses", "dataclass")
|
184
184
|
|
185
185
|
# Always use self-contained BaseSchema for client independence with automatic field mapping
|
186
|
-
# Use
|
187
|
-
|
188
|
-
# Already a relative import
|
189
|
-
core_import_path = f"{context.core_package_name}.schemas"
|
190
|
-
elif "." in context.core_package_name:
|
191
|
-
# External core package with dots (e.g., api_sdks.my_core) - use absolute import
|
192
|
-
core_import_path = f"{context.core_package_name}.schemas"
|
193
|
-
elif context.core_package_name == "core":
|
194
|
-
# Default relative core package
|
195
|
-
core_import_path = "..core.schemas"
|
196
|
-
else:
|
197
|
-
# Simple external core package name (e.g., shared_core_pkg) - use absolute import
|
198
|
-
core_import_path = f"{context.core_package_name}.schemas"
|
199
|
-
|
200
|
-
context.add_import(core_import_path, "BaseSchema")
|
186
|
+
# Use absolute core import path - add_import will handle it correctly
|
187
|
+
context.add_import(f"{context.core_package_name}.schemas", "BaseSchema")
|
201
188
|
|
202
189
|
# Add __all__ export
|
203
190
|
writer.write_line(f'__all__ = ["{class_name}"]')
|
@@ -80,7 +80,8 @@ class DataclassGenerator:
|
|
80
80
|
context.add_import("dataclasses", "dataclass")
|
81
81
|
context.add_import("dataclasses", "field")
|
82
82
|
context.add_import("typing", "Any")
|
83
|
-
|
83
|
+
# Use absolute core import path - add_import will handle it correctly
|
84
|
+
context.add_import(f"{context.core_package_name}.schemas", "BaseSchema")
|
84
85
|
|
85
86
|
description = schema.description or "Generic JSON value object that preserves arbitrary data."
|
86
87
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyopenapi-gen
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.3
|
4
4
|
Summary: Modern, async-first Python client generator for OpenAPI specifications with advanced cycle detection and unified type resolution
|
5
5
|
Project-URL: Homepage, https://github.com/your-org/pyopenapi-gen
|
6
6
|
Project-URL: Documentation, https://github.com/your-org/pyopenapi-gen/blob/main/README.md
|
@@ -7,7 +7,7 @@ pyopenapi_gen/py.typed,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
7
7
|
pyopenapi_gen/context/CLAUDE.md,sha256=eUPvSY2ADQK21i52bWfzyBcDPVvvepErMiQrq6ndwlU,9004
|
8
8
|
pyopenapi_gen/context/file_manager.py,sha256=vpbRByO5SH6COdjb6C-pXkdSIRu7QFqrXxi69VLKBnM,1691
|
9
9
|
pyopenapi_gen/context/import_collector.py,sha256=5WzQ5fXxARHJFZmYZ_GjPG5YjjzRLZTdN7jO2cbbk88,15225
|
10
|
-
pyopenapi_gen/context/render_context.py,sha256=
|
10
|
+
pyopenapi_gen/context/render_context.py,sha256=fAq0u3rDMZ0_OEHegrRUd98ySxs390XJbdypqQX8bwI,34894
|
11
11
|
pyopenapi_gen/core/CLAUDE.md,sha256=bz48K-PSrhxCq5ScmiLiU9kfpVVzSWRKOA9RdKk_pbg,6482
|
12
12
|
pyopenapi_gen/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
pyopenapi_gen/core/exceptions.py,sha256=HYFiYdmzsZUl46vB8M3B6Vpp6m8iqjUcKDWdL4yEKHo,498
|
@@ -64,7 +64,7 @@ pyopenapi_gen/core/parsing/transformers/inline_object_promoter.py,sha256=d9DZ1CV
|
|
64
64
|
pyopenapi_gen/core/writers/code_writer.py,sha256=5rWMPRdj5dWu3NRER6do4NEJEIa9qL68Hc7zhJ8jSdg,4763
|
65
65
|
pyopenapi_gen/core/writers/documentation_writer.py,sha256=ZnXJsRc9y9bUMsQR7g0qoBJZbBo2MCxHH_5KgxrPtyA,8645
|
66
66
|
pyopenapi_gen/core/writers/line_writer.py,sha256=-K2FaMtQa6hdzGZLcjQrT2ItEmfE-kquTCrn6R3I_QA,7743
|
67
|
-
pyopenapi_gen/core/writers/python_construct_renderer.py,sha256=
|
67
|
+
pyopenapi_gen/core/writers/python_construct_renderer.py,sha256=mJ8iCDQAJWg32ZaDI4U7eWRzym3bwzOrJ2VKAXOZds0,12066
|
68
68
|
pyopenapi_gen/core_package_template/README.md,sha256=8YP-MS0KxphRbCGBf7kV3dYIFLU9piOJ3IMm3K_0hcI,1488
|
69
69
|
pyopenapi_gen/emit/models_emitter.py,sha256=MBRq71UjtlZHrpf9QhDN0wXk_X-oGeGd6TAq3RKG7ko,7180
|
70
70
|
pyopenapi_gen/emitters/CLAUDE.md,sha256=iZYEZq1a1h033rxuh97cMpsKUElv72ysvTm3-QQUvrs,9323
|
@@ -122,11 +122,11 @@ pyopenapi_gen/visit/endpoint/processors/import_analyzer.py,sha256=ou5pl3S6PXvHrh
|
|
122
122
|
pyopenapi_gen/visit/endpoint/processors/parameter_processor.py,sha256=E0VoygkhU8iCsvH0U-tA6ZZg2Nm3rfr4e17vxqQLH7c,7666
|
123
123
|
pyopenapi_gen/visit/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
124
124
|
pyopenapi_gen/visit/model/alias_generator.py,sha256=wEMHipPA1_CFxvQ6CS9j4qgXK93seI1bI_tFJvIrb70,3563
|
125
|
-
pyopenapi_gen/visit/model/dataclass_generator.py,sha256=
|
125
|
+
pyopenapi_gen/visit/model/dataclass_generator.py,sha256=L794s2yyYUO__akGd120NJMV7g2xqiPfQyZo8CduIVM,14426
|
126
126
|
pyopenapi_gen/visit/model/enum_generator.py,sha256=AXqKUFuWUUjUF_6_HqBKY8vB5GYu35Pb2C2WPFrOw1k,10061
|
127
127
|
pyopenapi_gen/visit/model/model_visitor.py,sha256=TC6pbxpQiy5FWhmQpfllLuXA3ImTYNMcrazkOFZCIyo,9470
|
128
|
-
pyopenapi_gen-0.14.
|
129
|
-
pyopenapi_gen-0.14.
|
130
|
-
pyopenapi_gen-0.14.
|
131
|
-
pyopenapi_gen-0.14.
|
132
|
-
pyopenapi_gen-0.14.
|
128
|
+
pyopenapi_gen-0.14.3.dist-info/METADATA,sha256=sKA5cB3FHhSkY7wbQs8AlGg9GJc21uitec8EjIXbWtE,14025
|
129
|
+
pyopenapi_gen-0.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
130
|
+
pyopenapi_gen-0.14.3.dist-info/entry_points.txt,sha256=gxSlNiwom50T3OEZnlocA6qRjGdV0bn6hN_Xr-Ub5wA,56
|
131
|
+
pyopenapi_gen-0.14.3.dist-info/licenses/LICENSE,sha256=UFAyTWKa4w10-QerlJaHJeep7G2gcwpf-JmvI2dS2Gc,1088
|
132
|
+
pyopenapi_gen-0.14.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|