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.
@@ -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) # Core plain import
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 the core package from context - could be relative (..core) or absolute (api_sdks.my_core)
187
- if context.core_package_name.startswith(".."):
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
- context.add_import("..core.schemas", "BaseSchema")
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.2
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=07oINhHWi78qhcf-FNSOtojUrshmw0ZOTEgPpzBptbU,30271
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=aRLSiqUkf5Cp72BbJP7nVTmwPPrYlk8W66175joKAg0,12766
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=z0kKH_V2PJyiI9snmryDpAzRiOsQJtk1jrijCnEQQwg,14326
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.2.dist-info/METADATA,sha256=ERW_JKw-KdBCYXTQsG1sSXDYV9ILr9GgnKEvzfgHHNY,14025
129
- pyopenapi_gen-0.14.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
130
- pyopenapi_gen-0.14.2.dist-info/entry_points.txt,sha256=gxSlNiwom50T3OEZnlocA6qRjGdV0bn6hN_Xr-Ub5wA,56
131
- pyopenapi_gen-0.14.2.dist-info/licenses/LICENSE,sha256=UFAyTWKa4w10-QerlJaHJeep7G2gcwpf-JmvI2dS2Gc,1088
132
- pyopenapi_gen-0.14.2.dist-info/RECORD,,
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,,