Skip to content

Commit 453facc

Browse files
authored
Fix 18271: Circular imports on AllOf generation with REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=True (#18272)
* fix issue 18271 * the same update for python-pydantic-v1 * add test * update samples * update samples * put properties under allOf for python client tests * update samples * add a test in test model
1 parent 2c66356 commit 453facc

File tree

56 files changed

+2246
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2246
-0
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,11 @@ private ModelsMap postProcessModelsMap(ModelsMap objs) {
10001000
// set the extensions if the key is absent
10011001
model.getVendorExtensions().putIfAbsent("x-py-readonly", readOnlyFields);
10021002

1003+
// remove the items of postponedModelImports in modelImports to avoid circular imports error
1004+
if (!modelImports.isEmpty() && !postponedModelImports.isEmpty()){
1005+
modelImports.removeAll(postponedModelImports);
1006+
}
1007+
10031008
// import models one by one
10041009
if (!modelImports.isEmpty()) {
10051010
Set<String> modelsToImport = new TreeSet<>();

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonPydanticV1Codegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,11 @@ private ModelsMap postProcessModelsMap(ModelsMap objs) {
10021002
model.getVendorExtensions().putIfAbsent("x-py-datetime-imports", datetimeImports);
10031003
model.getVendorExtensions().putIfAbsent("x-py-readonly", readOnlyFields);
10041004

1005+
// remove the items of postponedModelImports in modelImports to avoid circular imports error
1006+
if (!modelImports.isEmpty() && !postponedModelImports.isEmpty()){
1007+
modelImports.removeAll(postponedModelImports);
1008+
}
1009+
10051010
// import models one by one
10061011
if (!modelImports.isEmpty()) {
10071012
Set<String> modelsToImport = new TreeSet<>();

modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,3 +2711,26 @@ components:
27112711
properties:
27122712
field:
27132713
type: string
2714+
CircularAllOfRef:
2715+
allOf:
2716+
- $ref: '#/components/schemas/AllOfSuperModel'
2717+
- properties:
2718+
secondCircularAllOfRef:
2719+
items:
2720+
$ref: '#/components/schemas/SecondCircularAllOfRef'
2721+
type: array
2722+
type: object
2723+
SecondCircularAllOfRef:
2724+
allOf:
2725+
- $ref: '#/components/schemas/AllOfSuperModel'
2726+
- properties:
2727+
circularAllOfRef:
2728+
items:
2729+
$ref: '#/components/schemas/CircularAllOfRef'
2730+
type: array
2731+
type: object
2732+
AllOfSuperModel:
2733+
properties:
2734+
_name:
2735+
type: string
2736+
type: object

samples/openapi3/client/petstore/python-aiohttp/.openapi-generator/FILES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ docs/AdditionalPropertiesAnyType.md
77
docs/AdditionalPropertiesClass.md
88
docs/AdditionalPropertiesObject.md
99
docs/AdditionalPropertiesWithDescriptionOnly.md
10+
docs/AllOfSuperModel.md
1011
docs/AllOfWithSingleRef.md
1112
docs/Animal.md
1213
docs/AnotherFakeApi.md
@@ -21,6 +22,7 @@ docs/Bathing.md
2122
docs/Capitalization.md
2223
docs/Cat.md
2324
docs/Category.md
25+
docs/CircularAllOfRef.md
2426
docs/CircularReferenceModel.md
2527
docs/ClassModel.md
2628
docs/Client.md
@@ -85,6 +87,7 @@ docs/PoopCleaning.md
8587
docs/PropertyMap.md
8688
docs/PropertyNameCollision.md
8789
docs/ReadOnlyFirst.md
90+
docs/SecondCircularAllOfRef.md
8891
docs/SecondRef.md
8992
docs/SelfReferenceModel.md
9093
docs/SingleRefType.md
@@ -125,6 +128,7 @@ petstore_api/models/additional_properties_any_type.py
125128
petstore_api/models/additional_properties_class.py
126129
petstore_api/models/additional_properties_object.py
127130
petstore_api/models/additional_properties_with_description_only.py
131+
petstore_api/models/all_of_super_model.py
128132
petstore_api/models/all_of_with_single_ref.py
129133
petstore_api/models/animal.py
130134
petstore_api/models/any_of_color.py
@@ -138,6 +142,7 @@ petstore_api/models/bathing.py
138142
petstore_api/models/capitalization.py
139143
petstore_api/models/cat.py
140144
petstore_api/models/category.py
145+
petstore_api/models/circular_all_of_ref.py
141146
petstore_api/models/circular_reference_model.py
142147
petstore_api/models/class_model.py
143148
petstore_api/models/client.py
@@ -197,6 +202,7 @@ petstore_api/models/poop_cleaning.py
197202
petstore_api/models/property_map.py
198203
petstore_api/models/property_name_collision.py
199204
petstore_api/models/read_only_first.py
205+
petstore_api/models/second_circular_all_of_ref.py
200206
petstore_api/models/second_ref.py
201207
petstore_api/models/self_reference_model.py
202208
petstore_api/models/single_ref_type.py

samples/openapi3/client/petstore/python-aiohttp/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ Class | Method | HTTP request | Description
154154
- [AdditionalPropertiesClass](docs/AdditionalPropertiesClass.md)
155155
- [AdditionalPropertiesObject](docs/AdditionalPropertiesObject.md)
156156
- [AdditionalPropertiesWithDescriptionOnly](docs/AdditionalPropertiesWithDescriptionOnly.md)
157+
- [AllOfSuperModel](docs/AllOfSuperModel.md)
157158
- [AllOfWithSingleRef](docs/AllOfWithSingleRef.md)
158159
- [Animal](docs/Animal.md)
159160
- [AnyOfColor](docs/AnyOfColor.md)
@@ -167,6 +168,7 @@ Class | Method | HTTP request | Description
167168
- [Capitalization](docs/Capitalization.md)
168169
- [Cat](docs/Cat.md)
169170
- [Category](docs/Category.md)
171+
- [CircularAllOfRef](docs/CircularAllOfRef.md)
170172
- [CircularReferenceModel](docs/CircularReferenceModel.md)
171173
- [ClassModel](docs/ClassModel.md)
172174
- [Client](docs/Client.md)
@@ -226,6 +228,7 @@ Class | Method | HTTP request | Description
226228
- [PropertyMap](docs/PropertyMap.md)
227229
- [PropertyNameCollision](docs/PropertyNameCollision.md)
228230
- [ReadOnlyFirst](docs/ReadOnlyFirst.md)
231+
- [SecondCircularAllOfRef](docs/SecondCircularAllOfRef.md)
229232
- [SecondRef](docs/SecondRef.md)
230233
- [SelfReferenceModel](docs/SelfReferenceModel.md)
231234
- [SingleRefType](docs/SingleRefType.md)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# AllOfSuperModel
2+
3+
4+
## Properties
5+
6+
Name | Type | Description | Notes
7+
------------ | ------------- | ------------- | -------------
8+
**name** | **str** | | [optional]
9+
10+
## Example
11+
12+
```python
13+
from petstore_api.models.all_of_super_model import AllOfSuperModel
14+
15+
# TODO update the JSON string below
16+
json = "{}"
17+
# create an instance of AllOfSuperModel from a JSON string
18+
all_of_super_model_instance = AllOfSuperModel.from_json(json)
19+
# print the JSON string representation of the object
20+
print(AllOfSuperModel.to_json())
21+
22+
# convert the object into a dict
23+
all_of_super_model_dict = all_of_super_model_instance.to_dict()
24+
# create an instance of AllOfSuperModel from a dict
25+
all_of_super_model_form_dict = all_of_super_model.from_dict(all_of_super_model_dict)
26+
```
27+
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
28+
29+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# CircularAllOfRef
2+
3+
4+
## Properties
5+
6+
Name | Type | Description | Notes
7+
------------ | ------------- | ------------- | -------------
8+
**name** | **str** | | [optional]
9+
**second_circular_all_of_ref** | [**List[SecondCircularAllOfRef]**](SecondCircularAllOfRef.md) | | [optional]
10+
11+
## Example
12+
13+
```python
14+
from petstore_api.models.circular_all_of_ref import CircularAllOfRef
15+
16+
# TODO update the JSON string below
17+
json = "{}"
18+
# create an instance of CircularAllOfRef from a JSON string
19+
circular_all_of_ref_instance = CircularAllOfRef.from_json(json)
20+
# print the JSON string representation of the object
21+
print(CircularAllOfRef.to_json())
22+
23+
# convert the object into a dict
24+
circular_all_of_ref_dict = circular_all_of_ref_instance.to_dict()
25+
# create an instance of CircularAllOfRef from a dict
26+
circular_all_of_ref_form_dict = circular_all_of_ref.from_dict(circular_all_of_ref_dict)
27+
```
28+
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
29+
30+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# SecondCircularAllOfRef
2+
3+
4+
## Properties
5+
6+
Name | Type | Description | Notes
7+
------------ | ------------- | ------------- | -------------
8+
**name** | **str** | | [optional]
9+
**circular_all_of_ref** | [**List[CircularAllOfRef]**](CircularAllOfRef.md) | | [optional]
10+
11+
## Example
12+
13+
```python
14+
from petstore_api.models.second_circular_all_of_ref import SecondCircularAllOfRef
15+
16+
# TODO update the JSON string below
17+
json = "{}"
18+
# create an instance of SecondCircularAllOfRef from a JSON string
19+
second_circular_all_of_ref_instance = SecondCircularAllOfRef.from_json(json)
20+
# print the JSON string representation of the object
21+
print(SecondCircularAllOfRef.to_json())
22+
23+
# convert the object into a dict
24+
second_circular_all_of_ref_dict = second_circular_all_of_ref_instance.to_dict()
25+
# create an instance of SecondCircularAllOfRef from a dict
26+
second_circular_all_of_ref_form_dict = second_circular_all_of_ref.from_dict(second_circular_all_of_ref_dict)
27+
```
28+
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
29+
30+

samples/openapi3/client/petstore/python-aiohttp/petstore_api/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from petstore_api.models.additional_properties_class import AdditionalPropertiesClass
4444
from petstore_api.models.additional_properties_object import AdditionalPropertiesObject
4545
from petstore_api.models.additional_properties_with_description_only import AdditionalPropertiesWithDescriptionOnly
46+
from petstore_api.models.all_of_super_model import AllOfSuperModel
4647
from petstore_api.models.all_of_with_single_ref import AllOfWithSingleRef
4748
from petstore_api.models.animal import Animal
4849
from petstore_api.models.any_of_color import AnyOfColor
@@ -56,6 +57,7 @@
5657
from petstore_api.models.capitalization import Capitalization
5758
from petstore_api.models.cat import Cat
5859
from petstore_api.models.category import Category
60+
from petstore_api.models.circular_all_of_ref import CircularAllOfRef
5961
from petstore_api.models.circular_reference_model import CircularReferenceModel
6062
from petstore_api.models.class_model import ClassModel
6163
from petstore_api.models.client import Client
@@ -115,6 +117,7 @@
115117
from petstore_api.models.property_map import PropertyMap
116118
from petstore_api.models.property_name_collision import PropertyNameCollision
117119
from petstore_api.models.read_only_first import ReadOnlyFirst
120+
from petstore_api.models.second_circular_all_of_ref import SecondCircularAllOfRef
118121
from petstore_api.models.second_ref import SecondRef
119122
from petstore_api.models.self_reference_model import SelfReferenceModel
120123
from petstore_api.models.single_ref_type import SingleRefType

samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from petstore_api.models.additional_properties_class import AdditionalPropertiesClass
1919
from petstore_api.models.additional_properties_object import AdditionalPropertiesObject
2020
from petstore_api.models.additional_properties_with_description_only import AdditionalPropertiesWithDescriptionOnly
21+
from petstore_api.models.all_of_super_model import AllOfSuperModel
2122
from petstore_api.models.all_of_with_single_ref import AllOfWithSingleRef
2223
from petstore_api.models.animal import Animal
2324
from petstore_api.models.any_of_color import AnyOfColor
@@ -31,6 +32,7 @@
3132
from petstore_api.models.capitalization import Capitalization
3233
from petstore_api.models.cat import Cat
3334
from petstore_api.models.category import Category
35+
from petstore_api.models.circular_all_of_ref import CircularAllOfRef
3436
from petstore_api.models.circular_reference_model import CircularReferenceModel
3537
from petstore_api.models.class_model import ClassModel
3638
from petstore_api.models.client import Client
@@ -90,6 +92,7 @@
9092
from petstore_api.models.property_map import PropertyMap
9193
from petstore_api.models.property_name_collision import PropertyNameCollision
9294
from petstore_api.models.read_only_first import ReadOnlyFirst
95+
from petstore_api.models.second_circular_all_of_ref import SecondCircularAllOfRef
9396
from petstore_api.models.second_ref import SecondRef
9497
from petstore_api.models.self_reference_model import SelfReferenceModel
9598
from petstore_api.models.single_ref_type import SingleRefType
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# coding: utf-8
2+
3+
"""
4+
OpenAPI Petstore
5+
6+
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
7+
8+
The version of the OpenAPI document: 1.0.0
9+
Generated by OpenAPI Generator (https://openapi-generator.tech)
10+
11+
Do not edit the class manually.
12+
""" # noqa: E501
13+
14+
15+
from __future__ import annotations
16+
import pprint
17+
import re # noqa: F401
18+
import json
19+
20+
from pydantic import BaseModel, ConfigDict, Field, StrictStr
21+
from typing import Any, ClassVar, Dict, List, Optional
22+
from typing import Optional, Set
23+
from typing_extensions import Self
24+
25+
class AllOfSuperModel(BaseModel):
26+
"""
27+
AllOfSuperModel
28+
""" # noqa: E501
29+
name: Optional[StrictStr] = Field(default=None, alias="_name")
30+
__properties: ClassVar[List[str]] = ["_name"]
31+
32+
model_config = ConfigDict(
33+
populate_by_name=True,
34+
validate_assignment=True,
35+
protected_namespaces=(),
36+
)
37+
38+
39+
def to_str(self) -> str:
40+
"""Returns the string representation of the model using alias"""
41+
return pprint.pformat(self.model_dump(by_alias=True))
42+
43+
def to_json(self) -> str:
44+
"""Returns the JSON representation of the model using alias"""
45+
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
46+
return json.dumps(self.to_dict())
47+
48+
@classmethod
49+
def from_json(cls, json_str: str) -> Optional[Self]:
50+
"""Create an instance of AllOfSuperModel from a JSON string"""
51+
return cls.from_dict(json.loads(json_str))
52+
53+
def to_dict(self) -> Dict[str, Any]:
54+
"""Return the dictionary representation of the model using alias.
55+
56+
This has the following differences from calling pydantic's
57+
`self.model_dump(by_alias=True)`:
58+
59+
* `None` is only added to the output dict for nullable fields that
60+
were set at model initialization. Other fields with value `None`
61+
are ignored.
62+
"""
63+
excluded_fields: Set[str] = set([
64+
])
65+
66+
_dict = self.model_dump(
67+
by_alias=True,
68+
exclude=excluded_fields,
69+
exclude_none=True,
70+
)
71+
return _dict
72+
73+
@classmethod
74+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
75+
"""Create an instance of AllOfSuperModel from a dict"""
76+
if obj is None:
77+
return None
78+
79+
if not isinstance(obj, dict):
80+
return cls.model_validate(obj)
81+
82+
_obj = cls.model_validate({
83+
"_name": obj.get("_name")
84+
})
85+
return _obj
86+
87+

0 commit comments

Comments
 (0)