From 1b82ec1b6cac3aa0b50fdc51292015dbbb77020c Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Wed, 7 Sep 2022 15:24:35 -0400 Subject: [PATCH 01/13] add discriminator --- openapi_python_client/parser/properties/__init__.py | 9 +++++++++ .../parser/properties/model_property.py | 8 +++++++- pyproject.toml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 2176c9033..241be374b 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -317,6 +317,13 @@ def build_model_property( optional_properties.append(prop) relative_imports.update(prop.get_imports(prefix="..")) + discriminator_mappings: Dict[str, Property] = {} + reference_name_to_subprop = {} + if data.discriminator is not None: + for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): + ref_class_name = Reference.from_ref(v).class_name + discriminator_mappings[k] = reference_name_to_subprop[ref_class_name] + additional_properties: Union[bool, Property, PropertyError] if data.additionalProperties is None: additional_properties = True @@ -347,6 +354,8 @@ def build_model_property( description=data.description or "", default=None, nullable=data.nullable, + discriminator_property=data.discriminator.propertyName if data.discriminator else None, + discriminator_mappings=discriminator_mappings, required=required, name=name, additional_properties=additional_properties, diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index c4c203a5d..09ed79d95 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterable -from typing import TYPE_CHECKING, ClassVar, Dict, List, Set, Union +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional, Set, Union import attr @@ -22,6 +22,9 @@ class ModelProperty(Property): references: List[oai.Reference] required_properties: List[Property] optional_properties: List[Property] + discriminator_property: Optional[str] + discriminator_mappings: Dict[str, Property] + description: str relative_imports: Set[str] additional_properties: Union[bool, Property] @@ -30,6 +33,9 @@ class ModelProperty(Property): template: ClassVar[str] = "model_property.pyi" json_is_dict: ClassVar[bool] = True + def __attrs_post_init__(self): + breakpoint() + def resolve_references( self, components: Dict[str, Union[oai.Reference, oai.Schema]], schemas: Schemas ) -> Union[Schemas, PropertyError]: diff --git a/pyproject.toml b/pyproject.toml index 3fba4ee1b..4f8ca5b7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "openapi-python-client" # Our versions have diverged and have no relation to upstream code changes # Henceforth, openapi-python-package will be maintained internally -version = "1.0.0" +version = "1.1.0" description = "Generate modern Python clients from OpenAPI" repository = "https://github.com/triaxtec/openapi-python-client" license = "MIT" From f700e1bac12c40e1bf9531e534d453315882dc2f Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Mon, 12 Sep 2022 16:17:54 -0400 Subject: [PATCH 02/13] initialize discriminator mappings --- openapi_python_client/parser/properties/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 241be374b..c9b64d087 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -318,11 +318,9 @@ def build_model_property( relative_imports.update(prop.get_imports(prefix="..")) discriminator_mappings: Dict[str, Property] = {} - reference_name_to_subprop = {} if data.discriminator is not None: for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): - ref_class_name = Reference.from_ref(v).class_name - discriminator_mappings[k] = reference_name_to_subprop[ref_class_name] + discriminator_mappings[k] = Reference.from_ref(v) additional_properties: Union[bool, Property, PropertyError] if data.additionalProperties is None: From 632f0aff12d504189b5885cea4421418e0f06da1 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Tue, 13 Sep 2022 14:01:17 -0400 Subject: [PATCH 03/13] getter for discriminator python_name --- .../parser/properties/model_property.py | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 09ed79d95..7b8594717 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -6,6 +6,7 @@ import attr from ... import schema as oai +from ... import utils from ..errors import PropertyError from ..reference import Reference from .property import Property @@ -33,9 +34,6 @@ class ModelProperty(Property): template: ClassVar[str] = "model_property.pyi" json_is_dict: ClassVar[bool] = True - def __attrs_post_init__(self): - breakpoint() - def resolve_references( self, components: Dict[str, Union[oai.Reference, oai.Schema]], schemas: Schemas ) -> Union[Schemas, PropertyError]: @@ -77,11 +75,39 @@ def resolve_references( self.optional_properties.append(prop) self.relative_imports.update(prop.get_imports(prefix="..")) + for _, value in self.discriminator_mappings.items(): + self.relative_imports.add(f"from ..models.{value.module_name} import {value.class_name}") + return schemas def get_base_type_string(self) -> str: return self.reference.class_name + def get_discriminator_name(self) -> str: + """ + Returns the python_name of the discriminator value, or None if there isn't one. + + discriminator_property is the name in openapi.yaml, for example 'nucleotideType' + This function returns its python_name, e.g. "nucleotide_type" + + Oligo: + discriminator: + propertyName: nucleotideType + mapping: + DNA: DnaOligo + RNA: RnaOligo + """ + if not self.discriminator_property: + return None + all_properties = self.optional_properties + self.required_properties + discriminator_reference = next(filter(lambda x: x.name == self.discriminator_property, all_properties), None) + if discriminator_reference: + return discriminator_reference.python_name + # self.discriminator_property is not a property of this model. + # If so, we assume it's a property inherited from oneOf or anyOf. + # Render it in snake_case since we cannot access it directly during template expansion. + return utils.snake_case(self.discriminator_property) + def get_imports(self, *, prefix: str) -> Set[str]: """ Get a set of import strings that should be included when this property is used somewhere From d8cbf024bfa6d81306ef9b0ca42ebc6fc0c53e50 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Thu, 15 Sep 2022 17:13:09 -0400 Subject: [PATCH 04/13] remove unused method --- .../parser/properties/model_property.py | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 7b8594717..65652ce28 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -6,7 +6,6 @@ import attr from ... import schema as oai -from ... import utils from ..errors import PropertyError from ..reference import Reference from .property import Property @@ -83,31 +82,6 @@ def resolve_references( def get_base_type_string(self) -> str: return self.reference.class_name - def get_discriminator_name(self) -> str: - """ - Returns the python_name of the discriminator value, or None if there isn't one. - - discriminator_property is the name in openapi.yaml, for example 'nucleotideType' - This function returns its python_name, e.g. "nucleotide_type" - - Oligo: - discriminator: - propertyName: nucleotideType - mapping: - DNA: DnaOligo - RNA: RnaOligo - """ - if not self.discriminator_property: - return None - all_properties = self.optional_properties + self.required_properties - discriminator_reference = next(filter(lambda x: x.name == self.discriminator_property, all_properties), None) - if discriminator_reference: - return discriminator_reference.python_name - # self.discriminator_property is not a property of this model. - # If so, we assume it's a property inherited from oneOf or anyOf. - # Render it in snake_case since we cannot access it directly during template expansion. - return utils.snake_case(self.discriminator_property) - def get_imports(self, *, prefix: str) -> Set[str]: """ Get a set of import strings that should be included when this property is used somewhere From 71a01b886fdfec98d2cacc8cc9a3ed18bccf1793 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Wed, 21 Sep 2022 14:31:16 -0400 Subject: [PATCH 05/13] fix discriminator.mapping null case --- openapi_python_client/parser/properties/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index c9b64d087..2bf356a87 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -319,7 +319,7 @@ def build_model_property( discriminator_mappings: Dict[str, Property] = {} if data.discriminator is not None: - for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): + for k, v in (data.discriminator.mapping or {}).items(): discriminator_mappings[k] = Reference.from_ref(v) additional_properties: Union[bool, Property, PropertyError] @@ -453,7 +453,7 @@ def build_union_property( discriminator_mappings: Dict[str, Property] = {} if data.discriminator is not None: - for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): + for k, v in (data.discriminator.mapping or {}).items(): ref_class_name = Reference.from_ref(v).class_name discriminator_mappings[k] = reference_name_to_subprop[ref_class_name] From d847f385f8bb4a404ce6699682c64ac7b1dec30f Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Fri, 23 Sep 2022 12:00:49 -0400 Subject: [PATCH 06/13] more inline discriminator properties --- openapi_python_client/parser/properties/property.py | 3 +++ openapi_python_client/templates/model.pyi | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index 2067e1539..a86e0ab8f 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -49,6 +49,9 @@ def get_type_string(self, no_optional: bool = False, query_parameter: bool = Fal """ if json: type_string = self._json_type_string + elif getattr(self, "discriminator_mappings", None): + discriminator_types = ", ".join([ref.class_name for ref in self.discriminator_mappings.values()]) + type_string = f"Union[{discriminator_types}]" else: type_string = self.get_base_type_string() diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index c286489e3..4e8e53c4c 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -13,6 +13,12 @@ from ..types import UNSET, Unset {{ relative }} {% endfor %} +{% for property in model.required_properties + model.optional_properties %} +{% if property.discriminator_property %} +{% for relative in property.relative_imports %} +{{ relative }} +{% endif %} +{% endfor %} {% if model.additional_properties %} {% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %} @@ -83,7 +89,11 @@ class {{ model.reference.class_name }}: {% else %} {% set property_source = 'd.pop("' + property.name + '", UNSET)' %} {% endif %} - {% if property.template %} + {% if property.discriminator_property %} + def get_{{ property.python_name}}() -> Union[Unset, None, {{ property.type_string }}]: + {% from "property_templates/discriminator_property.pyi" import construct %} + {{ construct(property, property_source) | indent(8) }} + {% elif property.template %} {% from "property_templates/" + property.template import construct %} {{ construct(property, property_source) | indent(8) }} {% else %} From 3c1b5b172956e4a25b6de80a7b75b81997a08d52 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Fri, 23 Sep 2022 12:02:56 -0400 Subject: [PATCH 07/13] add discriminator_property.pyi --- .../discriminator_property.pyi | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 openapi_python_client/templates/property_templates/discriminator_property.pyi diff --git a/openapi_python_client/templates/property_templates/discriminator_property.pyi b/openapi_python_client/templates/property_templates/discriminator_property.pyi new file mode 100644 index 000000000..dea3e30fd --- /dev/null +++ b/openapi_python_client/templates/property_templates/discriminator_property.pyi @@ -0,0 +1,27 @@ +{% macro construct(property, source, initial_value=None) %} + +{% if initial_value != None %} +{{ property.python_name }} = {{ initial_value }} +{% elif property.nullable %} +{{ property.python_name }} = None +{% else %} +{{ property.python_name }}: {{ property.get_type_string() }} = UNSET +{% endif %} + +_{{ property.python_name }} = {{source}} + +if {% if property.nullable %}_{{ property.python_name }} is not None{% endif %}{% if property.nullable and not property.required %} and {% endif %}{% if not property.required %}not isinstance(_{{ property.python_name }}, Unset){% endif %}: + + discriminator = d["{{ property.discriminator_property}}"] + {% set ns = namespace(if_stmt='if') %} + {% for (key, value) in property.discriminator_mappings.items() %} + {{ ns.if_stmt }} discriminator == "{{ key }}": + {{ property.python_name }} = {{ value.class_name }}.from_dict(_{{ property.python_name}}) + {% set ns.if_stmt = 'elif' %} + {% endfor %} + else: + raise ValueError(f'Unexpected discriminator value: {discriminator}') +{% endif %} +return {{ property.python_name }} + +{% endmacro %} From fce2d592955993e8813074e20e7e03408f74a248 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Fri, 23 Sep 2022 13:57:46 -0400 Subject: [PATCH 08/13] move discriminator property over to aurelia --- .../parser/properties/model_property.py | 3 +++ .../parser/properties/property.py | 3 --- openapi_python_client/templates/model.pyi | 12 +-------- .../discriminator_property.pyi | 27 ------------------- 4 files changed, 4 insertions(+), 41 deletions(-) delete mode 100644 openapi_python_client/templates/property_templates/discriminator_property.pyi diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 65652ce28..ea01336c2 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -80,6 +80,9 @@ def resolve_references( return schemas def get_base_type_string(self) -> str: + if getattr(self, "discriminator_mappings", None): + discriminator_types = ", ".join([ref.class_name for ref in self.discriminator_mappings.values()]) + return f"Union[{discriminator_types}]" return self.reference.class_name def get_imports(self, *, prefix: str) -> Set[str]: diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index a86e0ab8f..2067e1539 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -49,9 +49,6 @@ def get_type_string(self, no_optional: bool = False, query_parameter: bool = Fal """ if json: type_string = self._json_type_string - elif getattr(self, "discriminator_mappings", None): - discriminator_types = ", ".join([ref.class_name for ref in self.discriminator_mappings.values()]) - type_string = f"Union[{discriminator_types}]" else: type_string = self.get_base_type_string() diff --git a/openapi_python_client/templates/model.pyi b/openapi_python_client/templates/model.pyi index 4e8e53c4c..c286489e3 100644 --- a/openapi_python_client/templates/model.pyi +++ b/openapi_python_client/templates/model.pyi @@ -13,12 +13,6 @@ from ..types import UNSET, Unset {{ relative }} {% endfor %} -{% for property in model.required_properties + model.optional_properties %} -{% if property.discriminator_property %} -{% for relative in property.relative_imports %} -{{ relative }} -{% endif %} -{% endfor %} {% if model.additional_properties %} {% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %} @@ -89,11 +83,7 @@ class {{ model.reference.class_name }}: {% else %} {% set property_source = 'd.pop("' + property.name + '", UNSET)' %} {% endif %} - {% if property.discriminator_property %} - def get_{{ property.python_name}}() -> Union[Unset, None, {{ property.type_string }}]: - {% from "property_templates/discriminator_property.pyi" import construct %} - {{ construct(property, property_source) | indent(8) }} - {% elif property.template %} + {% if property.template %} {% from "property_templates/" + property.template import construct %} {{ construct(property, property_source) | indent(8) }} {% else %} diff --git a/openapi_python_client/templates/property_templates/discriminator_property.pyi b/openapi_python_client/templates/property_templates/discriminator_property.pyi deleted file mode 100644 index dea3e30fd..000000000 --- a/openapi_python_client/templates/property_templates/discriminator_property.pyi +++ /dev/null @@ -1,27 +0,0 @@ -{% macro construct(property, source, initial_value=None) %} - -{% if initial_value != None %} -{{ property.python_name }} = {{ initial_value }} -{% elif property.nullable %} -{{ property.python_name }} = None -{% else %} -{{ property.python_name }}: {{ property.get_type_string() }} = UNSET -{% endif %} - -_{{ property.python_name }} = {{source}} - -if {% if property.nullable %}_{{ property.python_name }} is not None{% endif %}{% if property.nullable and not property.required %} and {% endif %}{% if not property.required %}not isinstance(_{{ property.python_name }}, Unset){% endif %}: - - discriminator = d["{{ property.discriminator_property}}"] - {% set ns = namespace(if_stmt='if') %} - {% for (key, value) in property.discriminator_mappings.items() %} - {{ ns.if_stmt }} discriminator == "{{ key }}": - {{ property.python_name }} = {{ value.class_name }}.from_dict(_{{ property.python_name}}) - {% set ns.if_stmt = 'elif' %} - {% endfor %} - else: - raise ValueError(f'Unexpected discriminator value: {discriminator}') -{% endif %} -return {{ property.python_name }} - -{% endmacro %} From c8c30cd468a88423383d9ca338c78de9533a1d80 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Wed, 28 Sep 2022 15:30:44 -0400 Subject: [PATCH 09/13] handle unknowntype for discriminator properties --- openapi_python_client/parser/properties/model_property.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index ea01336c2..09ecc70b2 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -81,7 +81,9 @@ def resolve_references( def get_base_type_string(self) -> str: if getattr(self, "discriminator_mappings", None): - discriminator_types = ", ".join([ref.class_name for ref in self.discriminator_mappings.values()]) + discriminator_types = ", ".join( + [ref.class_name for ref in self.discriminator_mappings.values()] + ["UnknownType"] + ) return f"Union[{discriminator_types}]" return self.reference.class_name From d11fa4caea06c36e72a72a79b59d96775426d987 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Thu, 29 Sep 2022 16:03:25 -0400 Subject: [PATCH 10/13] walk oneOf as well as anyOf references --- openapi_python_client/parser/properties/model_property.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 09ecc70b2..f53685f3b 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -47,7 +47,7 @@ def resolve_references( assert isinstance(referenced_prop, oai.Schema) for p, val in (referenced_prop.properties or {}).items(): props[p] = (val, source_name) - for sub_prop in referenced_prop.allOf or []: + for sub_prop in referenced_prop.allOf or referenced_prop.oneOf or []: if isinstance(sub_prop, oai.Reference): self.references.append(sub_prop) else: From 230da4c91ce15a351687ff8f1633f8441163361d Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Mon, 3 Oct 2022 17:57:03 -0400 Subject: [PATCH 11/13] add parsing of toplevel UnionProperty --- openapi_python_client/.flake8 | 3 +++ openapi_python_client/__init__.py | 16 ++++++++++++---- .../parser/properties/__init__.py | 17 +++++++++++++++++ .../parser/properties/model_property.py | 6 +++++- 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 openapi_python_client/.flake8 diff --git a/openapi_python_client/.flake8 b/openapi_python_client/.flake8 new file mode 100644 index 000000000..b601d1408 --- /dev/null +++ b/openapi_python_client/.flake8 @@ -0,0 +1,3 @@ +[flake8] +per-file-ignores = + parser/properties/__init__.py: E402 diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index c6f1b884a..0125034f9 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -15,6 +15,7 @@ from .parser import GeneratorData, import_string_from_reference from .parser.errors import GeneratorError +from .parser.properties import UnionProperty from .utils import snake_case if sys.version_info.minor < 8: # version did not exist before 3.8, need to use a backport @@ -46,7 +47,6 @@ class Project: def __init__(self, *, openapi: GeneratorData, custom_template_path: Optional[Path] = None) -> None: self.openapi: GeneratorData = openapi - package_loader = PackageLoader(__package__) loader: BaseLoader if custom_template_path is not None: @@ -174,10 +174,18 @@ def _build_models(self) -> None: imports = [] model_template = self.env.get_template("model.pyi") + union_property_template = self.env.get_template("property_templates/union_property.pyi") + for model in self.openapi.models.values(): - module_path = models_dir / f"{model.reference.module_name}.py" - module_path.write_text(model_template.render(model=model)) - imports.append(import_string_from_reference(model.reference)) + if isinstance(model, UnionProperty): + template = union_property_template + else: + template = model_template + + module_path = models_dir / f"{model.module_name}.py" + module_path.write_text(template.render(model=model)) + if not isinstance(model, UnionProperty): + imports.append(import_string_from_reference(model.reference)) # Generate enums str_enum_template = self.env.get_template("str_enum.pyi") diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 2bf356a87..b3828d457 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -1,3 +1,5 @@ +_property = property # isort: skip + from itertools import chain from typing import Any, ClassVar, Dict, Generic, Iterable, Iterator, List, Optional, Set, Tuple, TypeVar, Union @@ -181,6 +183,13 @@ def _get_inner_type_strings(self, json: bool = False) -> List[str]: def get_base_type_string(self, json: bool = False) -> str: return f"Union[{', '.join(self._get_inner_type_strings(json=json))}]" + def resolve_references(self, components, schemas): + return schemas + + @_property + def module_name(self): + return self.name + def get_type_strings_in_union( self, no_optional: bool = False, query_parameter: bool = False, json: bool = False ) -> List[str]: @@ -284,6 +293,14 @@ def build_model_property( Used to infer the type name if a `title` property is not available. schemas: Existing Schemas which have already been processed (to check name conflicts) """ + if data.anyOf or data.oneOf: + prop, schemas = build_union_property( + data=data, name=name, required=required, schemas=schemas, parent_name=parent_name + ) + if not isinstance(prop, PropertyError): + schemas = attr.evolve(schemas, models={**schemas.models, prop.name: prop}) + return prop, schemas + required_set = set(data.required or []) required_properties: List[Property] = [] optional_properties: List[Property] = [] diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index f53685f3b..8a22df007 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -33,6 +33,10 @@ class ModelProperty(Property): template: ClassVar[str] = "model_property.pyi" json_is_dict: ClassVar[bool] = True + @property + def module_name(self): + return self.reference.module_name + def resolve_references( self, components: Dict[str, Union[oai.Reference, oai.Schema]], schemas: Schemas ) -> Union[Schemas, PropertyError]: @@ -47,7 +51,7 @@ def resolve_references( assert isinstance(referenced_prop, oai.Schema) for p, val in (referenced_prop.properties or {}).items(): props[p] = (val, source_name) - for sub_prop in referenced_prop.allOf or referenced_prop.oneOf or []: + for sub_prop in referenced_prop.allOf or referenced_prop.anyOf or referenced_prop.oneOf or []: if isinstance(sub_prop, oai.Reference): self.references.append(sub_prop) else: From 9afe1d2034f4d1a6d0b2f237d76fb2a8b5dcb980 Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Tue, 4 Oct 2022 10:58:30 -0400 Subject: [PATCH 12/13] support toplevel unionProperty --- openapi_python_client/__init__.py | 2 +- openapi_python_client/parser/properties/__init__.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py index 0125034f9..e4d3ac85e 100644 --- a/openapi_python_client/__init__.py +++ b/openapi_python_client/__init__.py @@ -174,7 +174,7 @@ def _build_models(self) -> None: imports = [] model_template = self.env.get_template("model.pyi") - union_property_template = self.env.get_template("property_templates/union_property.pyi") + union_property_template = self.env.get_template("polymorphic_model.pyi") for model in self.openapi.models.values(): if isinstance(model, UnionProperty): diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index b3828d457..2d6174c40 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -162,10 +162,11 @@ class UnionProperty(Property): """ A property representing a Union (anyOf) of other properties """ inner_properties: List[Property] + relative_imports: Set[str] = set() template: ClassVar[str] = "union_property.pyi" has_properties_without_templates: bool = attr.ib(init=False) - discriminator_property: Optional[str] - discriminator_mappings: Dict[str, Property] + discriminator_property: Optional[str] = None + discriminator_mappings: Dict[str, Property] = {} def __attrs_post_init__(self) -> None: super().__attrs_post_init__() @@ -184,11 +185,12 @@ def get_base_type_string(self, json: bool = False) -> str: return f"Union[{', '.join(self._get_inner_type_strings(json=json))}]" def resolve_references(self, components, schemas): + self.relative_imports.update(self.get_imports(prefix="..")) return schemas @_property def module_name(self): - return self.name + return self.python_name def get_type_strings_in_union( self, no_optional: bool = False, query_parameter: bool = False, json: bool = False From 36f4b87d5e1a187274e37bb2a6c267093be309cf Mon Sep 17 00:00:00 2001 From: Steve Strassmann Date: Wed, 5 Oct 2022 21:30:27 -0400 Subject: [PATCH 13/13] add nested parameter to construct macro --- .../templates/property_templates/date_property.pyi | 2 +- .../templates/property_templates/datetime_property.pyi | 2 +- .../templates/property_templates/dict_property.pyi | 2 +- .../templates/property_templates/enum_property.pyi | 2 +- .../templates/property_templates/file_property.pyi | 2 +- .../templates/property_templates/list_property.pyi | 2 +- .../templates/property_templates/model_property.pyi | 4 +++- .../templates/property_templates/none_property.pyi | 2 +- .../templates/property_templates/union_property.pyi | 4 +++- 9 files changed, 13 insertions(+), 9 deletions(-) diff --git a/openapi_python_client/templates/property_templates/date_property.pyi b/openapi_python_client/templates/property_templates/date_property.pyi index 528a9961b..209de81e0 100644 --- a/openapi_python_client/templates/property_templates/date_property.pyi +++ b/openapi_python_client/templates/property_templates/date_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="None") %} +{% macro construct(property, source, initial_value="None", nested=False) %} {% if property.required and not property.nullable %} {{ property.python_name }} = isoparse({{ source }}).date() {% else %} diff --git a/openapi_python_client/templates/property_templates/datetime_property.pyi b/openapi_python_client/templates/property_templates/datetime_property.pyi index 5442c75fe..6254d7bac 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.pyi +++ b/openapi_python_client/templates/property_templates/datetime_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="None") %} +{% macro construct(property, source, initial_value="None", nested=False) %} {% if property.required %} {% if property.nullable %} {{ property.python_name }} = {{ source }} diff --git a/openapi_python_client/templates/property_templates/dict_property.pyi b/openapi_python_client/templates/property_templates/dict_property.pyi index c4a853d72..4f45fd2e2 100644 --- a/openapi_python_client/templates/property_templates/dict_property.pyi +++ b/openapi_python_client/templates/property_templates/dict_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="None") %} +{% macro construct(property, source, initial_value="None", nested=False) %} {% if property.required %} {{ property.python_name }} = {{ source }} {% else %} diff --git a/openapi_python_client/templates/property_templates/enum_property.pyi b/openapi_python_client/templates/property_templates/enum_property.pyi index 4c33ba051..6d71cf602 100644 --- a/openapi_python_client/templates/property_templates/enum_property.pyi +++ b/openapi_python_client/templates/property_templates/enum_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="None") %} +{% macro construct(property, source, initial_value="None", nested=False) %} {% if property.required %} {{ property.python_name }} = {{ property.reference.class_name }}({{ source }}) {% else %} diff --git a/openapi_python_client/templates/property_templates/file_property.pyi b/openapi_python_client/templates/property_templates/file_property.pyi index 1758c07f6..90d850a32 100644 --- a/openapi_python_client/templates/property_templates/file_property.pyi +++ b/openapi_python_client/templates/property_templates/file_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value=None) %} +{% macro construct(property, source, initial_value=None, nested=False) %} {{ property.python_name }} = File( payload = BytesIO({{ source }}) ) diff --git a/openapi_python_client/templates/property_templates/list_property.pyi b/openapi_python_client/templates/property_templates/list_property.pyi index 0705e9d93..f5f3d5592 100644 --- a/openapi_python_client/templates/property_templates/list_property.pyi +++ b/openapi_python_client/templates/property_templates/list_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="[]") %} +{% macro construct(property, source, initial_value="[]", nested=False) %} {% set inner_property = property.inner_property %} {% if inner_property.template %} {% set inner_source = inner_property.python_name + "_data" %} diff --git a/openapi_python_client/templates/property_templates/model_property.pyi b/openapi_python_client/templates/property_templates/model_property.pyi index ee819f8a6..91f4bba13 100644 --- a/openapi_python_client/templates/property_templates/model_property.pyi +++ b/openapi_python_client/templates/property_templates/model_property.pyi @@ -1,4 +1,6 @@ -{% macro construct(property, source, initial_value=None) %} +{# This file is shadowed by the template with the same name + # in aurelia/packages/api_client_generation/templates #} +{% macro construct(property, source, initial_value=None, nested=False) %} {% if property.required and not property.nullable %} {% if source == "response.yaml" %} yaml_dict = yaml.safe_load(response.text.encode("utf-8")) diff --git a/openapi_python_client/templates/property_templates/none_property.pyi b/openapi_python_client/templates/property_templates/none_property.pyi index 235530c8b..a2dee93ca 100644 --- a/openapi_python_client/templates/property_templates/none_property.pyi +++ b/openapi_python_client/templates/property_templates/none_property.pyi @@ -1,4 +1,4 @@ -{% macro construct(property, source, initial_value="None") %} +{% macro construct(property, source, initial_value="None", nested=False) %} {{ property.python_name }} = {{ initial_value }} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/union_property.pyi b/openapi_python_client/templates/property_templates/union_property.pyi index 179dd4ae3..e12540d9d 100644 --- a/openapi_python_client/templates/property_templates/union_property.pyi +++ b/openapi_python_client/templates/property_templates/union_property.pyi @@ -1,4 +1,6 @@ -{% macro construct(property, source, initial_value=None) %} +{# This file is shadowed by the template with the same name + # in aurelia/packages/api_client_generation/templates #} +{% macro construct(property, source, initial_value=None, nested=False) %} def _parse_{{ property.python_name }}(data: {{ property.get_type_string(json=True) }}) -> {{ property.get_type_string() }}: {{ property.python_name }}: {{ property.get_type_string() }} {% if "None" in property.get_type_strings_in_union(json=True) %}