From 1d4fa964a19dcf37a3136345a44ee0f785402b24 Mon Sep 17 00:00:00 2001 From: George Murray Date: Mon, 20 Sep 2021 09:35:56 -0700 Subject: [PATCH 1/8] Discriminator support --- .../parser/properties/__init__.py | 17 +++++++++++++++++ .../property_templates/union_property.pyi | 11 +++++++++++ 2 files changed, 28 insertions(+) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index f6d9e3626..fbd395d63 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -162,6 +162,8 @@ class UnionProperty(Property): inner_properties: List[Property] template: ClassVar[str] = "union_property.pyi" has_properties_without_templates: bool = attr.ib(init=False) + discriminator_property: Optional[str] + discriminator_mappings: Dict[str, Property] def __attrs_post_init__(self) -> None: super().__attrs_post_init__() @@ -423,15 +425,28 @@ def build_union_property( *, data: oai.Schema, name: str, required: bool, schemas: Schemas, parent_name: str ) -> Tuple[Union[UnionProperty, PropertyError], Schemas]: sub_properties: List[Property] = [] + inverted_mappings = {} + for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): + inverted_mappings[Reference.from_ref(v).class_name] = k + discriminator_mappings: Dict[str, Property] = {} for sub_prop_data in chain(data.anyOf, data.oneOf): sub_prop, schemas = property_from_data( name=name, required=required, data=sub_prop_data, schemas=schemas, parent_name=parent_name ) + if name == "entities_item": + print("subprop", sub_prop) if isinstance(sub_prop, PropertyError): return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), schemas + sub_properties.append(sub_prop) + if data.discriminator is not None: + discriminated_by = inverted_mappings.get(sub_prop.reference.class_name) + if discriminated_by is not None: + discriminator_mappings[discriminated_by] = sub_prop default = convert_chain((prop._type_string for prop in sub_properties), data.default) + if name == "entities_item": + print(f"UNION ({name}): ", data) return ( UnionProperty( name=name, @@ -439,6 +454,8 @@ def build_union_property( default=default, inner_properties=sub_properties, nullable=data.nullable, + discriminator_property=data.discriminator.propertyName if data.discriminator else None, + discriminator_mappings=discriminator_mappings ), schemas, ) diff --git a/openapi_python_client/templates/property_templates/union_property.pyi b/openapi_python_client/templates/property_templates/union_property.pyi index 179dd4ae3..24b51c917 100644 --- a/openapi_python_client/templates/property_templates/union_property.pyi +++ b/openapi_python_client/templates/property_templates/union_property.pyi @@ -9,6 +9,17 @@ def _parse_{{ property.python_name }}(data: {{ property.get_type_string(json=Tru if isinstance(data, Unset): return data {% endif %} + {% if property.discriminator_property != None %} + discriminator_value: str = data.get("{{property.discriminator_property}}") + if discriminator_value is not None: + {% for discriminated_by, inner_property in property.discriminator_mappings.items() %} + if discriminator_value == "{{discriminated_by}}": + {% from "property_templates/" + inner_property.template import construct %} + {{ construct(inner_property, "data", initial_value="UNSET") | indent(8) }} + return {{ property.python_name }} + {% endfor %} + + {% endif %} {% for inner_property in property.inner_properties_with_template() %} {% if not loop.last or property.has_properties_without_templates %} try: From 0632dfff59d5021e77b5acd5a0675993dea0e6de Mon Sep 17 00:00:00 2001 From: George Murray Date: Mon, 20 Sep 2021 09:39:39 -0700 Subject: [PATCH 2/8] Remove debugging prints --- openapi_python_client/parser/properties/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index fbd395d63..e2f641556 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -433,8 +433,6 @@ def build_union_property( sub_prop, schemas = property_from_data( name=name, required=required, data=sub_prop_data, schemas=schemas, parent_name=parent_name ) - if name == "entities_item": - print("subprop", sub_prop) if isinstance(sub_prop, PropertyError): return PropertyError(detail=f"Invalid property in union {name}", data=sub_prop_data), schemas @@ -445,8 +443,6 @@ def build_union_property( discriminator_mappings[discriminated_by] = sub_prop default = convert_chain((prop._type_string for prop in sub_properties), data.default) - if name == "entities_item": - print(f"UNION ({name}): ", data) return ( UnionProperty( name=name, From fd1f32b602708e9e65d50647115bc58dfc146a8a Mon Sep 17 00:00:00 2001 From: George Murray Date: Thu, 23 Sep 2021 23:13:40 -0700 Subject: [PATCH 3/8] WIP --- openapi_python_client/parser/properties/__init__.py | 2 ++ openapi_python_client/templates/endpoint_module.pyi | 1 + 2 files changed, 3 insertions(+) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index e2f641556..9f953c1a2 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -183,6 +183,8 @@ def get_type_strings_in_union( self, no_optional: bool = False, query_parameter: bool = False, json: bool = False ) -> List[str]: type_strings = self._get_inner_type_strings(json=json) + if not json: + type_strings.append("UnknownType") if no_optional: return type_strings if self.required: diff --git a/openapi_python_client/templates/endpoint_module.pyi b/openapi_python_client/templates/endpoint_module.pyi index 29dfadc46..fbe9c98ac 100644 --- a/openapi_python_client/templates/endpoint_module.pyi +++ b/openapi_python_client/templates/endpoint_module.pyi @@ -5,6 +5,7 @@ from attr import asdict from ...client import AuthenticatedClient, Client from ...types import Response +from ...extensions import UnknownType {% for relative in endpoint.relative_imports %} {{ relative }} From bd5a1bf433f0e3bf9c9f54f677808a46bf493636 Mon Sep 17 00:00:00 2001 From: George Murray Date: Fri, 24 Sep 2021 01:39:55 -0700 Subject: [PATCH 4/8] Move json checkk to inner type --- 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 9f953c1a2..251c0d173 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -173,6 +173,8 @@ def __attrs_post_init__(self) -> None: def _get_inner_type_strings(self, json: bool = False) -> List[str]: inner_types = [p.get_type_string(no_optional=True, json=json) for p in self.inner_properties] + if not json: + inner_types.append("UnknownType") unique_inner_types = list(dict.fromkeys(inner_types)) return unique_inner_types @@ -183,8 +185,6 @@ def get_type_strings_in_union( self, no_optional: bool = False, query_parameter: bool = False, json: bool = False ) -> List[str]: type_strings = self._get_inner_type_strings(json=json) - if not json: - type_strings.append("UnknownType") if no_optional: return type_strings if self.required: From ad3ed2a9116b95dc502c30e6dd72d6bd586b41f3 Mon Sep 17 00:00:00 2001 From: George Murray Date: Fri, 24 Sep 2021 09:10:22 -0700 Subject: [PATCH 5/8] Move template to benchling-sdk --- .../templates/property_templates/union_property.pyi | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/openapi_python_client/templates/property_templates/union_property.pyi b/openapi_python_client/templates/property_templates/union_property.pyi index 24b51c917..179dd4ae3 100644 --- a/openapi_python_client/templates/property_templates/union_property.pyi +++ b/openapi_python_client/templates/property_templates/union_property.pyi @@ -9,17 +9,6 @@ def _parse_{{ property.python_name }}(data: {{ property.get_type_string(json=Tru if isinstance(data, Unset): return data {% endif %} - {% if property.discriminator_property != None %} - discriminator_value: str = data.get("{{property.discriminator_property}}") - if discriminator_value is not None: - {% for discriminated_by, inner_property in property.discriminator_mappings.items() %} - if discriminator_value == "{{discriminated_by}}": - {% from "property_templates/" + inner_property.template import construct %} - {{ construct(inner_property, "data", initial_value="UNSET") | indent(8) }} - return {{ property.python_name }} - {% endfor %} - - {% endif %} {% for inner_property in property.inner_properties_with_template() %} {% if not loop.last or property.has_properties_without_templates %} try: From 0195d846b732758e6e7c661fe0138ae516737d47 Mon Sep 17 00:00:00 2001 From: George Murray Date: Fri, 24 Sep 2021 09:12:36 -0700 Subject: [PATCH 6/8] Revert endpoint change --- openapi_python_client/templates/endpoint_module.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi_python_client/templates/endpoint_module.pyi b/openapi_python_client/templates/endpoint_module.pyi index fbe9c98ac..29dfadc46 100644 --- a/openapi_python_client/templates/endpoint_module.pyi +++ b/openapi_python_client/templates/endpoint_module.pyi @@ -5,7 +5,6 @@ from attr import asdict from ...client import AuthenticatedClient, Client from ...types import Response -from ...extensions import UnknownType {% for relative in endpoint.relative_imports %} {{ relative }} From 03c3dc23dd53523c8d28bbff85c3690095cc4dc9 Mon Sep 17 00:00:00 2001 From: George Murray Date: Fri, 24 Sep 2021 16:09:40 -0700 Subject: [PATCH 7/8] Avoid unioning if for properties without templates, only allow one class mapping in discriminator --- openapi_python_client/parser/properties/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 251c0d173..5cd145df9 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -173,7 +173,7 @@ def __attrs_post_init__(self) -> None: def _get_inner_type_strings(self, json: bool = False) -> List[str]: inner_types = [p.get_type_string(no_optional=True, json=json) for p in self.inner_properties] - if not json: + if not json and not self.has_properties_without_templates: inner_types.append("UnknownType") unique_inner_types = list(dict.fromkeys(inner_types)) return unique_inner_types @@ -429,6 +429,11 @@ def build_union_property( sub_properties: List[Property] = [] inverted_mappings = {} for k, v in (data.discriminator.mapping if data.discriminator else {}).items(): + class_name = Reference.from_ref(v).class_name + if class_name in inverted_mappings: + raise ArgumentError( + f"Mapping more than one name to a class is currently not supported (class: {class_name})." + ) inverted_mappings[Reference.from_ref(v).class_name] = k discriminator_mappings: Dict[str, Property] = {} for sub_prop_data in chain(data.anyOf, data.oneOf): From 32d7c190d6c7922b387a077d71a53f5b0e0d1ab0 Mon Sep 17 00:00:00 2001 From: George Murray Date: Fri, 24 Sep 2021 18:45:26 -0700 Subject: [PATCH 8/8] properties without templates can now have UnknownType --- openapi_python_client/parser/properties/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index 5cd145df9..eb8afa223 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -173,7 +173,7 @@ def __attrs_post_init__(self) -> None: def _get_inner_type_strings(self, json: bool = False) -> List[str]: inner_types = [p.get_type_string(no_optional=True, json=json) for p in self.inner_properties] - if not json and not self.has_properties_without_templates: + if not json: inner_types.append("UnknownType") unique_inner_types = list(dict.fromkeys(inner_types)) return unique_inner_types