Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[python] Fix Circular imports on inherited discriminators. #17886

Merged
merged 12 commits into from
Feb 20, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,7 @@ private ModelsMap postProcessModelsMap(ModelsMap objs) {
// if super class
if (model.getDiscriminator() != null && model.getDiscriminator().getMappedModels() != null) {
moduleImports.add("typing", "Union");
Set<CodegenDiscriminator.MappedModel> discriminator = model.getDiscriminator().getMappedModels();
for (CodegenDiscriminator.MappedModel mappedModel : discriminator) {
postponedModelImports.add(mappedModel.getModelName());
}
moduleImports.add("importlib", "import_module");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -879,10 +879,6 @@ private ModelsMap postProcessModelsMap(ModelsMap objs) {
// if super class
if (model.getDiscriminator() != null && model.getDiscriminator().getMappedModels() != null) {
typingImports.add("Union");
Set<CodegenDiscriminator.MappedModel> discriminator = model.getDiscriminator().getMappedModels();
for (CodegenDiscriminator.MappedModel mappedModel : discriminator) {
postponedModelImports.add(mappedModel.getModelName());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ import json
{{{.}}}
{{/vendorExtensions.x-py-model-imports}}

{{#hasChildren}}
{{#discriminator}}
{{! If this model is a super class, importlib is used. So import the necessary modules for the type here. }}
from typing import TYPE_CHECKING
from importlib import import_module
if TYPE_CHECKING:
{{#mappedModels}}
from {{packageName}}.models.{{model.classVarName}} import {{modelName}}
{{/mappedModels}}

{{/discriminator}}
{{/hasChildren}}
class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}):
"""
{{#description}}{{{description}}} # noqa: E501{{/description}}{{^description}}{{{classname}}}{{/description}}
Expand Down Expand Up @@ -222,13 +234,13 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{#discriminator}}
# look up the object type based on discriminator mapping
object_type = cls.get_discriminator_value(obj)
if object_type:
klass = globals()[object_type]
return klass.from_dict(obj)
else:
raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))
{{#mappedModels}}
if object_type == '{{{mappingName}}}':
return import_module("{{packageName}}.models.{{model.classVarName}}").{{modelName}}.from_dict(obj)
{{/mappedModels}}
raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))
{{/discriminator}}
{{/hasChildren}}
{{^hasChildren}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ import json
from typing import Optional, Set
from typing_extensions import Self

{{#hasChildren}}
{{#discriminator}}
{{! If this model is a super class, importlib is used. So import the necessary modules for the type here. }}
from typing import TYPE_CHECKING
if TYPE_CHECKING:
{{#mappedModels}}
from {{packageName}}.models.{{model.classVarName}} import {{modelName}}
{{/mappedModels}}

{{/discriminator}}
{{/hasChildren}}
class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}):
"""
{{#description}}{{{description}}}{{/description}}{{^description}}{{{classname}}}{{/description}}
Expand Down Expand Up @@ -113,7 +124,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
return json.dumps(self.to_dict())

@classmethod
def from_json(cls, json_str: str) -> Optional[{{^hasChildren}}Self{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union[{{#children}}Self{{^-last}}, {{/-last}}{{/children}}]{{/discriminator}}{{^discriminator}}Self{{/discriminator}}{{/hasChildren}}]:
def from_json(cls, json_str: str) -> Optional[{{^hasChildren}}Self{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union[{{#mappedModels}}{{{modelName}}}{{^-last}}, {{/-last}}{{/mappedModels}}]{{/discriminator}}{{^discriminator}}Self{{/discriminator}}{{/hasChildren}}]:
"""Create an instance of {{{classname}}} from a JSON string"""
return cls.from_dict(json.loads(json_str))

Expand Down Expand Up @@ -236,18 +247,19 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}

{{#hasChildren}}
@classmethod
def from_dict(cls, obj: Dict[str, Any]) -> Optional[{{#discriminator}}Union[{{#children}}Self{{^-last}}, {{/-last}}{{/children}}]{{/discriminator}}{{^discriminator}}Self{{/discriminator}}]:
def from_dict(cls, obj: Dict[str, Any]) -> Optional[{{#discriminator}}Union[{{#mappedModels}}{{{modelName}}}{{^-last}}, {{/-last}}{{/mappedModels}}]{{/discriminator}}{{^discriminator}}Self{{/discriminator}}]:
"""Create an instance of {{{classname}}} from a dict"""
{{#discriminator}}
# look up the object type based on discriminator mapping
object_type = cls.get_discriminator_value(obj)
if object_type:
klass = globals()[object_type]
return klass.from_dict(obj)
else:
raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))
{{#mappedModels}}
if object_type == '{{{mappingName}}}':
return import_module("{{packageName}}.models.{{model.classVarName}}").{{modelName}}.from_dict(obj)
{{/mappedModels}}

raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))
{{/discriminator}}
{{/hasChildren}}
{{^hasChildren}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,20 @@ components:
required:
- id
- activity
DiscriminatorAllOfSuper:
type: object
required:
- elementType
discriminator:
propertyName: elementType
properties:
elementType:
type: string
DiscriminatorAllOfSub:
allOf:
- $ref: '#/components/schemas/DiscriminatorAllOfSuper'
- type: object
properties: {}
Pet:
type: object
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ docs/CreatureInfo.md
docs/DanishPig.md
docs/DefaultApi.md
docs/DeprecatedObject.md
docs/DiscriminatorAllOfSub.md
docs/DiscriminatorAllOfSuper.md
docs/Dog.md
docs/DummyModel.md
docs/EnumArrays.md
Expand Down Expand Up @@ -140,6 +142,8 @@ petstore_api/models/creature.py
petstore_api/models/creature_info.py
petstore_api/models/danish_pig.py
petstore_api/models/deprecated_object.py
petstore_api/models/discriminator_all_of_sub.py
petstore_api/models/discriminator_all_of_super.py
petstore_api/models/dog.py
petstore_api/models/dummy_model.py
petstore_api/models/enum_arrays.py
Expand Down
2 changes: 2 additions & 0 deletions samples/openapi3/client/petstore/python-aiohttp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ Class | Method | HTTP request | Description
- [CreatureInfo](docs/CreatureInfo.md)
- [DanishPig](docs/DanishPig.md)
- [DeprecatedObject](docs/DeprecatedObject.md)
- [DiscriminatorAllOfSub](docs/DiscriminatorAllOfSub.md)
- [DiscriminatorAllOfSuper](docs/DiscriminatorAllOfSuper.md)
- [Dog](docs/Dog.md)
- [DummyModel](docs/DummyModel.md)
- [EnumArrays](docs/EnumArrays.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# DiscriminatorAllOfSub


## Properties

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------

## Example

```python
from petstore_api.models.discriminator_all_of_sub import DiscriminatorAllOfSub

# TODO update the JSON string below
json = "{}"
# create an instance of DiscriminatorAllOfSub from a JSON string
discriminator_all_of_sub_instance = DiscriminatorAllOfSub.from_json(json)
# print the JSON string representation of the object
print DiscriminatorAllOfSub.to_json()

# convert the object into a dict
discriminator_all_of_sub_dict = discriminator_all_of_sub_instance.to_dict()
# create an instance of DiscriminatorAllOfSub from a dict
discriminator_all_of_sub_form_dict = discriminator_all_of_sub.from_dict(discriminator_all_of_sub_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# DiscriminatorAllOfSuper


## Properties

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**element_type** | **str** | |

## Example

```python
from petstore_api.models.discriminator_all_of_super import DiscriminatorAllOfSuper

# TODO update the JSON string below
json = "{}"
# create an instance of DiscriminatorAllOfSuper from a JSON string
discriminator_all_of_super_instance = DiscriminatorAllOfSuper.from_json(json)
# print the JSON string representation of the object
print DiscriminatorAllOfSuper.to_json()

# convert the object into a dict
discriminator_all_of_super_dict = discriminator_all_of_super_instance.to_dict()
# create an instance of DiscriminatorAllOfSuper from a dict
discriminator_all_of_super_form_dict = discriminator_all_of_super.from_dict(discriminator_all_of_super_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
from petstore_api.models.creature_info import CreatureInfo
from petstore_api.models.danish_pig import DanishPig
from petstore_api.models.deprecated_object import DeprecatedObject
from petstore_api.models.discriminator_all_of_sub import DiscriminatorAllOfSub
from petstore_api.models.discriminator_all_of_super import DiscriminatorAllOfSuper
from petstore_api.models.dog import Dog
from petstore_api.models.dummy_model import DummyModel
from petstore_api.models.enum_arrays import EnumArrays
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
from petstore_api.models.creature_info import CreatureInfo
from petstore_api.models.danish_pig import DanishPig
from petstore_api.models.deprecated_object import DeprecatedObject
from petstore_api.models.discriminator_all_of_sub import DiscriminatorAllOfSub
from petstore_api.models.discriminator_all_of_super import DiscriminatorAllOfSuper
from petstore_api.models.dog import Dog
from petstore_api.models.dummy_model import DummyModel
from petstore_api.models.enum_arrays import EnumArrays
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
import re # noqa: F401
import json

from importlib import import_module
from pydantic import BaseModel, Field, StrictStr
from typing import Any, ClassVar, Dict, List, Optional, Union
from typing import Optional, Set
from typing_extensions import Self

from typing import TYPE_CHECKING
if TYPE_CHECKING:
from petstore_api.models.cat import Cat
from petstore_api.models.dog import Dog

class Animal(BaseModel):
"""
Animal
Expand Down Expand Up @@ -64,7 +70,7 @@ def to_json(self) -> str:
return json.dumps(self.to_dict())

@classmethod
def from_json(cls, json_str: str) -> Optional[Union[Self, Self]]:
def from_json(cls, json_str: str) -> Optional[Union[Cat, Dog]]:
"""Create an instance of Animal from a JSON string"""
return cls.from_dict(json.loads(json_str))

Expand All @@ -89,20 +95,17 @@ def to_dict(self) -> Dict[str, Any]:
return _dict

@classmethod
def from_dict(cls, obj: Dict[str, Any]) -> Optional[Union[Self, Self]]:
def from_dict(cls, obj: Dict[str, Any]) -> Optional[Union[Cat, Dog]]:
"""Create an instance of Animal from a dict"""
# look up the object type based on discriminator mapping
object_type = cls.get_discriminator_value(obj)
if object_type:
klass = globals()[object_type]
return klass.from_dict(obj)
else:
raise ValueError("Animal failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))

from petstore_api.models.cat import Cat
from petstore_api.models.dog import Dog
# TODO: Rewrite to not use raise_errors
Animal.model_rebuild(raise_errors=False)
if object_type == 'Cat':
return import_module("petstore_api.models.cat").Cat.from_dict(obj)
if object_type == 'Dog':
return import_module("petstore_api.models.dog").Dog.from_dict(obj)

raise ValueError("Animal failed to lookup discriminator value from " +
json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
", mapping: " + json.dumps(cls.__discriminator_value_class_map))


Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# coding: utf-8

"""
OpenAPI Petstore

This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\

The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
""" # noqa: E501


from __future__ import annotations
import pprint
import re # noqa: F401
import json

from typing import Any, ClassVar, Dict, List
from petstore_api.models.discriminator_all_of_super import DiscriminatorAllOfSuper
from typing import Optional, Set
from typing_extensions import Self

class DiscriminatorAllOfSub(DiscriminatorAllOfSuper):
"""
DiscriminatorAllOfSub
""" # noqa: E501
__properties: ClassVar[List[str]] = ["elementType"]

model_config = {
"populate_by_name": True,
"validate_assignment": True,
"protected_namespaces": (),
}


def to_str(self) -> str:
"""Returns the string representation of the model using alias"""
return pprint.pformat(self.model_dump(by_alias=True))

def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())

@classmethod
def from_json(cls, json_str: str) -> Optional[Self]:
"""Create an instance of DiscriminatorAllOfSub from a JSON string"""
return cls.from_dict(json.loads(json_str))

def to_dict(self) -> Dict[str, Any]:
"""Return the dictionary representation of the model using alias.

This has the following differences from calling pydantic's
`self.model_dump(by_alias=True)`:

* `None` is only added to the output dict for nullable fields that
were set at model initialization. Other fields with value `None`
are ignored.
"""
excluded_fields: Set[str] = set([
])

_dict = self.model_dump(
by_alias=True,
exclude=excluded_fields,
exclude_none=True,
)
return _dict

@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of DiscriminatorAllOfSub from a dict"""
if obj is None:
return None

if not isinstance(obj, dict):
return cls.model_validate(obj)

_obj = cls.model_validate({
"elementType": obj.get("elementType")
})
return _obj


Loading
Loading