From f469553d42eb1d977064af60343c88737f09d3f8 Mon Sep 17 00:00:00 2001 From: Forest Tong Date: Wed, 27 Jan 2021 18:09:24 -0500 Subject: [PATCH 1/5] Cherry-pick optional params --- end_to_end_tests/openapi.json | 87 +++++----- .../parser/properties/__init__.py | 94 +++++----- .../parser/properties/enum_property.py | 14 +- .../parser/properties/model_property.py | 69 +------- .../parser/properties/property.py | 49 ++++-- .../templates/endpoint_macros.pyi | 17 +- .../property_templates/date_property.pyi | 6 +- .../property_templates/datetime_property.pyi | 6 +- .../property_templates/enum_property.pyi | 10 +- .../property_templates/file_property.pyi | 6 +- .../property_templates/list_property.pyi | 8 +- .../property_templates/model_property.pyi | 10 +- .../property_templates/union_property.pyi | 10 +- openapi_python_client/templates/types.py | 5 +- pyproject.toml | 16 +- .../test_parser/test_properties/test_init.py | 163 ++++++++---------- .../test_properties/test_model_property.py | 146 +--------------- .../test_date_property/optional_nullable.py | 2 +- 18 files changed, 254 insertions(+), 464 deletions(-) diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 8562f6796..931fc481e 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -290,12 +290,49 @@ { "required": false, "schema": { - "title": "Datetime Prop", + "title": "Not Required, Not Nullable Datetime Prop", + "nullable": false, "type": "string", "format": "date-time", "default": "1010-10-10T00:00:00" }, - "name": "datetime_prop", + "name": "not_required_not_nullable_datetime_prop", + "in": "query" + }, + { + "required": false, + "schema": { + "title": "Not Required, Nullable Datetime Prop", + "nullable": true, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "not_required_nullable_datetime_prop", + "in": "query" + }, + { + "required": true, + "schema": { + "title": "Required, Not Nullable Datetime Prop", + "nullable": false, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "required_not_nullable_datetime_prop", + "in": "query" + }, + { + "required": true, + "schema": { + "title": "Required, Nullable Datetime Prop", + "nullable": true, + "type": "string", + "format": "date-time", + "default": "1010-10-10T00:00:00" + }, + "name": "required_nullable_datetime_prop", "in": "query" }, { @@ -396,14 +433,6 @@ }, "name": "enum_prop", "in": "query" - }, - { - "required": false, - "schema": { - "$ref": "#/components/schemas/ModelWithUnionProperty" - }, - "name": "model_prop", - "in": "query" } ], "responses": { @@ -630,7 +659,7 @@ "schemas": { "AModel": { "title": "AModel", - "required": ["an_enum_value", "aCamelDateTime", "a_date", "a_nullable_date", "required_nullable", "required_not_nullable", "model", "nullable_model"], + "required": ["an_enum_value", "aCamelDateTime", "a_date", "a_nullable_date", "required_nullable", "required_not_nullable"], "type": "object", "properties": { "an_enum_value": { @@ -694,42 +723,6 @@ "title": "NOT Required AND NOT Nullable", "type": "string", "nullable": false - }, - "model": { - "type": "object", - "allOf": [ - { - "ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": false - }, - "nullable_model": { - "type": "object", - "allOf": [ - { - "ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": true - }, - "not_required_model": { - "type": "object", - "allOf": [ - { - "ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": false - }, - "not_required_nullable_model": { - "type": "object", - "allOf": [ - { - "ref": "#/components/schemas/ModelWithUnionProperty" - } - ], - "nullable": true } }, "description": "A Model for testing all the ways custom objects can be used ", diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index f6289ce7c..e3a3f69b7 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -19,6 +19,7 @@ class NoneProperty(Property): """ A property that is always None (used for empty schemas) """ _type_string: ClassVar[str] = "None" + _json_type_string: ClassVar[str] = "None" template: ClassVar[Optional[str]] = "none_property.pyi" @@ -29,6 +30,7 @@ class StringProperty(Property): max_length: Optional[int] = None pattern: Optional[str] = None _type_string: ClassVar[str] = "str" + _json_type_string: ClassVar[str] = "str" @attr.s(auto_attribs=True, frozen=True) @@ -38,6 +40,7 @@ class DateTimeProperty(Property): """ _type_string: ClassVar[str] = "datetime.datetime" + _json_type_string: ClassVar[str] = "str" template: ClassVar[str] = "datetime_property.pyi" def get_imports(self, *, prefix: str) -> Set[str]: @@ -58,6 +61,7 @@ class DateProperty(Property): """ A property of type datetime.date """ _type_string: ClassVar[str] = "datetime.date" + _json_type_string: ClassVar[str] = "str" template: ClassVar[str] = "date_property.pyi" def get_imports(self, *, prefix: str) -> Set[str]: @@ -78,6 +82,8 @@ class FileProperty(Property): """ A property used for uploading files """ _type_string: ClassVar[str] = "File" + # Return type of File.to_tuple() + _json_type_string: ClassVar[str] = "Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]" template: ClassVar[str] = "file_property.pyi" def get_imports(self, *, prefix: str) -> Set[str]: @@ -98,6 +104,7 @@ class FloatProperty(Property): """ A property of type float """ _type_string: ClassVar[str] = "float" + _json_type_string: ClassVar[str] = "float" @attr.s(auto_attribs=True, frozen=True) @@ -105,6 +112,7 @@ class IntProperty(Property): """ A property of type int """ _type_string: ClassVar[str] = "int" + _json_type_string: ClassVar[str] = "int" @attr.s(auto_attribs=True, frozen=True) @@ -112,6 +120,7 @@ class BooleanProperty(Property): """ Property for bool """ _type_string: ClassVar[str] = "bool" + _json_type_string: ClassVar[str] = "bool" InnerProp = TypeVar("InnerProp", bound=Property) @@ -122,18 +131,11 @@ class ListProperty(Property, Generic[InnerProp]): """ A property representing a list (array) of other properties """ inner_property: InnerProp + _json_type_string: ClassVar[str] = "List[Any]" template: ClassVar[str] = "list_property.pyi" - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - type_string = f"List[{self.inner_property.get_type_string()}]" - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + def get_base_type_string(self) -> str: + return f"List[{self.inner_property.get_type_string()}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -167,18 +169,38 @@ def __attrs_post_init__(self) -> None: self, "has_properties_without_templates", any(prop.template is None for prop in self.inner_properties) ) - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - inner_types = [p.get_type_string(no_optional=True) for p in self.inner_properties] - inner_prop_string = ", ".join(inner_types) - type_string = f"Union[{inner_prop_string}]" + def _get_inner_prop_string(self, json: bool = False) -> str: + inner_types = [p.get_type_string(no_optional=True, json=json) for p in self.inner_properties] + unique_inner_types = list(dict.fromkeys(inner_types)) + return ", ".join(unique_inner_types) + + def get_base_type_string(self, json: bool = False) -> str: + return f"Union[{self._get_inner_prop_string(json=json)}]" + + def get_type_string(self, no_optional: bool = False, query_parameter: bool = False, json: bool = False) -> str: + """ + Get a string representation of type that should be used when declaring this property. + + This implementation differs slightly from `Property.get_type_string` in order to collapse + nested union types. + """ + type_string = self.get_base_type_string(json=json) if no_optional: return type_string - if not self.required: - type_string = f"Union[Unset, {inner_prop_string}]" - if self.nullable: - type_string = f"Optional[{type_string}]" - return type_string + if self.required: + if self.nullable: + return f"Union[None, {self._get_inner_prop_string(json=json)}]" + else: + return type_string + else: + if self.nullable: + return f"Union[Unset, None, {self._get_inner_prop_string(json=json)}]" + else: + if query_parameter: + # For query parameters, None has the same meaning as Unset + return f"Union[Unset, None, {self._get_inner_prop_string(json=json)}]" + else: + return f"Union[Unset, {self._get_inner_prop_string(json=json)}]" def get_imports(self, *, prefix: str) -> Set[str]: """ @@ -250,23 +272,13 @@ def build_model_property( required_properties: List[Property] = [] optional_properties: List[Property] = [] relative_imports: Set[str] = set() - references: List[oai.Reference] = [] class_name = data.title or name if parent_name: class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}" ref = Reference.from_ref(class_name) - all_props = data.properties or {} - if not isinstance(data, oai.Reference) and data.allOf: - for sub_prop in data.allOf: - if isinstance(sub_prop, oai.Reference): - references += [sub_prop] - else: - all_props.update(sub_prop.properties or {}) - required_set.update(sub_prop.required or []) - - for key, value in all_props.items(): + for key, value in (data.properties or {}).items(): prop_required = key in required_set prop, schemas = property_from_data( name=key, required=prop_required, data=value, schemas=schemas, parent_name=class_name @@ -302,7 +314,6 @@ def build_model_property( prop = ModelProperty( reference=ref, - references=references, required_properties=required_properties, optional_properties=optional_properties, relative_imports=relative_imports, @@ -460,6 +471,9 @@ def _property_from_data( ) if data.anyOf or data.oneOf: return build_union_property(data=data, name=name, required=required, schemas=schemas, parent_name=parent_name) + if not data.type: + return NoneProperty(name=name, required=required, nullable=False, default=None), schemas + if data.type == "string": return _string_based_property(name=name, required=required, data=data), schemas elif data.type == "number": @@ -494,10 +508,8 @@ def _property_from_data( ) elif data.type == "array": return build_list_property(data=data, name=name, required=required, schemas=schemas, parent_name=parent_name) - elif data.type == "object" or data.allOf: + elif data.type == "object": return build_model_property(data=data, name=name, schemas=schemas, required=required, parent_name=parent_name) - elif not data.type: - return NoneProperty(name=name, required=required, nullable=False, default=None), schemas return PropertyError(data=data, detail=f"unknown type {data.type}"), schemas @@ -554,16 +566,6 @@ def build_schemas(*, components: Dict[str, Union[oai.Reference, oai.Schema]]) -> schemas = schemas_or_err processing = True # We made some progress this round, do another after it's done to_process = next_round - - resolve_errors: List[PropertyError] = [] - models = list(schemas.models.values()) - for model in models: - schemas_or_err = model.resolve_references(components=components, schemas=schemas) - if isinstance(schemas_or_err, PropertyError): - resolve_errors.append(schemas_or_err) - else: - schemas = schemas_or_err - schemas.errors.extend(errors) - schemas.errors.extend(resolve_errors) + return schemas diff --git a/openapi_python_client/parser/properties/enum_property.py b/openapi_python_client/parser/properties/enum_property.py index 1217f23ee..6938dd716 100644 --- a/openapi_python_client/parser/properties/enum_property.py +++ b/openapi_python_client/parser/properties/enum_property.py @@ -18,21 +18,13 @@ class EnumProperty(Property): values: Dict[str, ValueType] reference: Reference value_type: Type[ValueType] + _json_type_string: ClassVar[str] = "int" default: Optional[Any] = attr.ib() template: ClassVar[str] = "enum_property.pyi" - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - - type_string = self.reference.class_name - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + def get_base_type_string(self) -> str: + return self.reference.class_name def get_imports(self, *, prefix: str) -> Set[str]: """ diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 4bcbe4695..3ab145af4 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -1,87 +1,28 @@ -from __future__ import annotations - -from collections.abc import Iterable -from typing import TYPE_CHECKING, ClassVar, Dict, List, Set, Union +from typing import ClassVar, List, Set, Union import attr -from ... import schema as oai -from ..errors import PropertyError from ..reference import Reference from .property import Property -if TYPE_CHECKING: - from .schemas import Schemas - @attr.s(auto_attribs=True, frozen=True) class ModelProperty(Property): """ A property which refers to another Schema """ reference: Reference - references: List[oai.Reference] + required_properties: List[Property] optional_properties: List[Property] description: str relative_imports: Set[str] additional_properties: Union[bool, Property] + _json_type_string: ClassVar[str] = "Dict[str, Any]" template: ClassVar[str] = "model_property.pyi" - json_is_dict: ClassVar[bool] = True - - def resolve_references( - self, components: Dict[str, Union[oai.Reference, oai.Schema]], schemas: Schemas - ) -> Union[Schemas, PropertyError]: - from ..properties import property_from_data - - required_set = set() - props = {} - while self.references: - reference = self.references.pop() - source_name = Reference.from_ref(reference.ref).class_name - referenced_prop = components[source_name] - 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 []: - if isinstance(sub_prop, oai.Reference): - self.references.append(sub_prop) - else: - for p, val in (sub_prop.properties or {}).items(): - props[p] = (val, source_name) - if isinstance(referenced_prop.required, Iterable): - for sub_prop_name in referenced_prop.required: - required_set.add(sub_prop_name) - - for key, (value, source_name) in (props or {}).items(): - required = key in required_set - prop, schemas = property_from_data( - name=key, required=required, data=value, schemas=schemas, parent_name=source_name - ) - if isinstance(prop, PropertyError): - return prop - if required: - self.required_properties.append(prop) - # Remove the optional version - new_optional_props = [op for op in self.optional_properties if op.name != prop.name] - self.optional_properties.clear() - self.optional_properties.extend(new_optional_props) - elif not any(ep for ep in (self.optional_properties + self.required_properties) if ep.name == prop.name): - self.optional_properties.append(prop) - self.relative_imports.update(prop.get_imports(prefix="..")) - - return schemas - def get_type_string(self, no_optional: bool = False) -> str: - """ Get a string representation of type that should be used when declaring this property """ - type_string = self.reference.class_name - if no_optional: - return type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[{type_string}, Unset]" - return type_string + def get_base_type_string(self) -> str: + 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 a94af72ba..651370ae2 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -24,30 +24,48 @@ class Property: required: bool nullable: bool _type_string: ClassVar[str] = "" + _json_type_string: ClassVar[str] = "" # Type of the property after JSON serialization default: Optional[str] = attr.ib() python_name: str = attr.ib(init=False) template: ClassVar[Optional[str]] = None - json_is_dict: ClassVar[bool] = False def __attrs_post_init__(self) -> None: object.__setattr__(self, "python_name", utils.to_valid_python_identifier(utils.snake_case(self.name))) - def get_type_string(self, no_optional: bool = False) -> str: + def get_base_type_string(self) -> str: + return self._type_string + + def get_type_string(self, no_optional: bool = False, query_parameter: bool = False, json: bool = False) -> str: """ Get a string representation of type that should be used when declaring this property Args: no_optional: Do not include Optional or Unset even if the value is optional (needed for isinstance checks) + query_parameter: True if the property's type is being used for a query parameter + json: True if the type refers to the property after JSON serialization """ - type_string = self._type_string + if json: + type_string = self._json_type_string + else: + type_string = self.get_base_type_string() + if no_optional: - return self._type_string - if self.nullable: - type_string = f"Optional[{type_string}]" - if not self.required: - type_string = f"Union[Unset, {type_string}]" - return type_string + return type_string + if self.required: + if self.nullable: + return f"Optional[{type_string}]" + else: + return type_string + else: + if self.nullable: + return f"Union[Unset, None, {type_string}]" + else: + if query_parameter: + # For query parameters, None has the same meaning as Unset + return f"Union[Unset, None, {type_string}]" + else: + return f"Union[Unset, {type_string}]" def get_instance_type_string(self) -> str: """Get a string representation of runtime type that should be used for `isinstance` checks""" @@ -70,8 +88,13 @@ def get_imports(self, *, prefix: str) -> Set[str]: imports.add(f"from {prefix}types import UNSET, Unset") return imports - def to_string(self) -> str: - """ How this should be declared in a dataclass """ + def to_string(self, query_parameter: bool = False) -> str: + """ + How this should be declared in a dataclass + + Args: + query_parameter: True if the property's type is being used for a query parameter + """ default: Optional[str] if self.default is not None: default = self.default @@ -81,6 +104,6 @@ def to_string(self) -> str: default = None if default is not None: - return f"{self.python_name}: {self.get_type_string()} = {default}" + return f"{self.python_name}: {self.get_type_string(query_parameter=query_parameter)} = {default}" else: - return f"{self.python_name}: {self.get_type_string()}" + return f"{self.python_name}: {self.get_type_string(query_parameter=query_parameter)}" diff --git a/openapi_python_client/templates/endpoint_macros.pyi b/openapi_python_client/templates/endpoint_macros.pyi index 8d3d464c3..7a1862be3 100644 --- a/openapi_python_client/templates/endpoint_macros.pyi +++ b/openapi_python_client/templates/endpoint_macros.pyi @@ -17,12 +17,12 @@ if {{ parameter.python_name }} is not UNSET: {% set destination = "json_" + property.python_name %} {% if property.template %} {% from "property_templates/" + property.template import transform %} -{{ transform(property, property.python_name, destination) }} +{{ transform(property, property.python_name, destination, query_parameter=True) }} {% endif %} {% endfor %} params: Dict[str, Any] = { {% for property in endpoint.query_parameters %} - {% if property.required %} + {% if property.required and not property.nullable %} {% if property.template %} "{{ property.name }}": {{ "json_" + property.python_name }}, {% else %} @@ -32,13 +32,12 @@ params: Dict[str, Any] = { {% endfor %} } {% for property in endpoint.query_parameters %} - {% if not property.required %} - {% set property_name = "json_" + property.python_name if property.template else property.python_name %} -if {% if not property.required %}not isinstance({{ property_name }}, Unset) and {% endif %}{{ property_name }} is not None: - {% if property.json_is_dict %} - params.update({{ property_name }}) + {% if not property.required or property.nullable %} +if {% if not property.required %}{{ property.python_name }} is not UNSET and {% endif %}{{ property.python_name }} is not None: + {% if property.template %} + params["{{ property.name }}"] = {{ "json_" + property.python_name }} {% else %} - params["{{ property.name }}"] = {{ property_name }} + params["{{ property.name }}"] = {{ property.python_name }} {% endif %} {% endif %} {% endfor %} @@ -97,7 +96,7 @@ json_body: {{ endpoint.json_body.get_type_string() }}, {% endif %} {# query parameters #} {% for parameter in endpoint.query_parameters %} -{{ parameter.to_string() }}, +{{ parameter.to_string(query_parameter=True) }}, {% endfor %} {% for parameter in endpoint.header_parameters %} {{ parameter.to_string() }}, diff --git a/openapi_python_client/templates/property_templates/date_property.pyi b/openapi_python_client/templates/property_templates/date_property.pyi index a3a980c8f..bc2cae912 100644 --- a/openapi_python_client/templates/property_templates/date_property.pyi +++ b/openapi_python_client/templates/property_templates/date_property.pyi @@ -9,13 +9,13 @@ if _{{ property.python_name }} is not None: {% endif %} {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% if property.required %} {{ destination }} = {{ source }}.isoformat() {% if property.nullable %}if {{ source }} else None {%endif%} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, str]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} +{% if property.nullable or query_parameter %} {{ destination }} = {{ source }}.isoformat() if {{ source }} else None {% else %} {{ destination }} = {{ source }}.isoformat() diff --git a/openapi_python_client/templates/property_templates/datetime_property.pyi b/openapi_python_client/templates/property_templates/datetime_property.pyi index b8e1b8ff0..91ce0cacc 100644 --- a/openapi_python_client/templates/property_templates/datetime_property.pyi +++ b/openapi_python_client/templates/property_templates/datetime_property.pyi @@ -14,7 +14,7 @@ if _{{ property.python_name }} is not None: {% endif %} {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% if property.required %} {% if property.nullable %} {{ destination }} = {{ source }}.isoformat() if {{ source }} else None @@ -22,9 +22,9 @@ if _{{ property.python_name }} is not None: {{ destination }} = {{ source }}.isoformat() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, str]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} +{% if property.nullable or query_parameter %} {{ destination }} = {{ source }}.isoformat() if {{ source }} else None {% else %} {{ destination }} = {{ source }}.isoformat() diff --git a/openapi_python_client/templates/property_templates/enum_property.pyi b/openapi_python_client/templates/property_templates/enum_property.pyi index 4765a6fd5..831f633d5 100644 --- a/openapi_python_client/templates/property_templates/enum_property.pyi +++ b/openapi_python_client/templates/property_templates/enum_property.pyi @@ -9,7 +9,7 @@ if _{{ property.python_name }} is not None: {% endif %} {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% if property.required %} {% if property.nullable %} {{ destination }} = {{ source }}.value if {{ source }} else None @@ -17,12 +17,12 @@ if _{{ property.python_name }} is not None: {{ destination }} = {{ source }}.value {% endif %} {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} - {{ destination }} = {{ source }} if {{ source }} else None +{% if property.nullable or query_parameter %} + {{ destination }} = {{ source }}.value if {{ source }} else None {% else %} - {{ destination }} = {{ source }} + {{ destination }} = {{ source }}.value {% endif %} {% endif %} {% endmacro %} diff --git a/openapi_python_client/templates/property_templates/file_property.pyi b/openapi_python_client/templates/property_templates/file_property.pyi index ffa3c20d9..50a331851 100644 --- a/openapi_python_client/templates/property_templates/file_property.pyi +++ b/openapi_python_client/templates/property_templates/file_property.pyi @@ -4,7 +4,7 @@ ) {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% if property.required %} {% if property.nullable %} {{ destination }} = {{ source }}.to_tuple() if {{ source }} else None @@ -12,9 +12,9 @@ {{ destination }} = {{ source }}.to_tuple() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} +{% if property.nullable or query_parameter %} {{ destination }} = {{ source }}.to_tuple() if {{ source }} else None {% else %} {{ destination }} = {{ source }}.to_tuple() diff --git a/openapi_python_client/templates/property_templates/list_property.pyi b/openapi_python_client/templates/property_templates/list_property.pyi index d05a13960..5f58bcd30 100644 --- a/openapi_python_client/templates/property_templates/list_property.pyi +++ b/openapi_python_client/templates/property_templates/list_property.pyi @@ -32,7 +32,7 @@ for {{ inner_source }} in {{ source }}: {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% set inner_property = property.inner_property %} {% if property.required %} {% if property.nullable %} @@ -44,13 +44,13 @@ else: {{ _transform(property, source, destination) }} {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[Unset, List[Any]]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} +{% if property.nullable or query_parameter %} if {{ source }} is None: {{ destination }} = None else: - {{ _transform(property, source, destination) | indent(4)}} + {{ _transform(property, source, destination) | indent(8)}} {% else %} {{ _transform(property, source, destination) | indent(4)}} {% endif %} diff --git a/openapi_python_client/templates/property_templates/model_property.pyi b/openapi_python_client/templates/property_templates/model_property.pyi index b41289409..dfda61d97 100644 --- a/openapi_python_client/templates/property_templates/model_property.pyi +++ b/openapi_python_client/templates/property_templates/model_property.pyi @@ -1,5 +1,5 @@ {% macro construct(property, source, initial_value=None) %} -{% if property.required and not property.nullable %} +{% if property.required %} {{ property.python_name }} = {{ property.reference.class_name }}.from_dict({{ source }}) {% else %} {% if initial_value != None %} @@ -10,12 +10,12 @@ {{ 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 %}: +if _{{ property.python_name }} is not None and not isinstance(_{{ property.python_name }}, Unset): {{ property.python_name }} = {{ property.reference.class_name }}.from_dict(cast(Dict[str, Any], _{{ property.python_name }})) {% endif %} {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} {% if property.required %} {% if property.nullable %} {{ destination }} = {{ source }}.to_dict() if {{ source }} else None @@ -23,9 +23,9 @@ if {% if property.nullable %}_{{ property.python_name }} is not None{% endif %}{ {{ destination }} = {{ source }}.to_dict() {% endif %} {% else %} -{{ destination }}{% if declare_type %}: Union[{% if property.nullable %}None, {% endif %}Unset, Dict[str, Any]]{% endif %} = UNSET +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} = UNSET if not isinstance({{ source }}, Unset): -{% if property.nullable %} +{% if property.nullable or query_parameter %} {{ destination }} = {{ source }}.to_dict() if {{ source }} else None {% else %} {{ destination }} = {{ source }}.to_dict() diff --git a/openapi_python_client/templates/property_templates/union_property.pyi b/openapi_python_client/templates/property_templates/union_property.pyi index 4c632c60a..509c7f34e 100644 --- a/openapi_python_client/templates/property_templates/union_property.pyi +++ b/openapi_python_client/templates/property_templates/union_property.pyi @@ -24,20 +24,20 @@ def _parse_{{ property.python_name }}(data: Any) -> {{ property.get_type_string( {{ property.python_name }} = _parse_{{ property.python_name }}({{ source }}) {% endmacro %} -{% macro transform(property, source, destination, declare_type=True) %} -{% if not property.required %} -{{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} +{% macro transform(property, source, destination, declare_type=True, query_parameter=False) %} +{% if not property.required or property.nullable %} +{{ destination }}{% if declare_type %}: {{ property.get_type_string(query_parameter=query_parameter, json=True) }}{% endif %} if isinstance({{ source }}, Unset): {{ destination }} = UNSET {% endif %} -{% if property.nullable %} +{% if property.nullable or (query_parameter and not property.required) %} {% if property.required %} if {{ source }} is None: {% else %}{# There's an if UNSET statement before this #} elif {{ source }} is None: {% endif %} - {{ destination }}{% if declare_type %}: {{ property.get_type_string() }}{% endif %} = None + {{ destination }} = None {% endif %} {% for inner_property in property.inner_properties_with_template() %} {% if loop.first and property.required and not property.nullable %}{# No if UNSET or if None statement before this #} diff --git a/openapi_python_client/templates/types.py b/openapi_python_client/templates/types.py index 2061b9f08..a354a2192 100644 --- a/openapi_python_client/templates/types.py +++ b/openapi_python_client/templates/types.py @@ -11,6 +11,9 @@ def __bool__(self) -> bool: UNSET: Unset = Unset() +# Used as `FileProperty._json_type_string` +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +23,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type diff --git a/pyproject.toml b/pyproject.toml index 222b69561..4be8d93db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ safety = "*" pytest-cov = "*" python-multipart = "*" flake8 = "*" +typer-cli = "^0.0.11" [tool.taskipy.tasks] check = """ @@ -55,8 +56,9 @@ isort .\ && flake8 openapi_python_client\ && safety check --bare\ && mypy openapi_python_client\ - && pytest --cov openapi_python_client tests --cov-report=term-missing\ + && task unit\ """ +unit = "pytest --cov openapi_python_client tests --cov-report=term-missing" regen = "python -m end_to_end_tests.regen_golden_record" regen_custom = "python -m end_to_end_tests.regen_golden_record custom" e2e = "pytest openapi_python_client end_to_end_tests/test_end_to_end.py" @@ -65,12 +67,7 @@ task regen\ && task regen_custom\ && task e2e\ """ -gen-setuppy = """ -poetry build \ -&& tar --strip-components=1 -xvf "$(ls -1 dist/*tar.gz | tail -1)" '*/setup.py' \ -&& isort setup.py \ -&& black setup.py -""" +docs = "typer openapi_python_client/cli.py utils docs > usage.md" [tool.black] line-length = 120 @@ -98,8 +95,5 @@ skip = [".venv", "tests/test_templates"] omit = ["openapi_python_client/templates/*"] [build-system] -requires = [ - "setuptools>=30.3.0,<50", - "poetry>=1.0" -] +requires = ["poetry>=1.0"] build-backend = "poetry.masonry.api" diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 2f3c13676..2f1c71cc5 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -16,45 +16,55 @@ class TestProperty: - def test_get_type_string(self, mocker): + @pytest.mark.parametrize( + "query_parameter,nullable,required,no_optional,expected", + [ + (False, False, False, False, "Union[Unset, TestType]"), + (False, False, False, True, "TestType"), + (False, False, True, False, "TestType"), + (False, False, True, True, "TestType"), + (False, True, False, False, "Union[Unset, None, TestType]"), + (False, True, False, True, "TestType"), + (False, True, True, False, "Optional[TestType]"), + (False, True, True, True, "TestType"), + (True, False, False, False, "Union[Unset, None, TestType]"), + (True, False, False, True, "TestType"), + (True, False, True, False, "TestType"), + (True, False, True, True, "TestType"), + (True, True, False, False, "Union[Unset, None, TestType]"), + (True, True, False, True, "TestType"), + (True, True, True, False, "Optional[TestType]"), + (True, True, True, True, "TestType"), + ], + ) + def test_get_type_string(self, mocker, query_parameter, nullable, required, no_optional, expected): from openapi_python_client.parser.properties import Property mocker.patch.object(Property, "_type_string", "TestType") - p = Property(name="test", required=True, default=None, nullable=False) - - base_type_string = f"TestType" - - assert p.get_type_string() == base_type_string + p = Property(name="test", required=required, default=None, nullable=nullable) + assert p.get_type_string(no_optional=no_optional, query_parameter=query_parameter) == expected - p = Property(name="test", required=True, default=None, nullable=True) - assert p.get_type_string() == f"Optional[{base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = Property(name="test", required=False, default=None, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = Property(name="test", required=False, default=None, nullable=False) - assert p.get_type_string() == f"Union[Unset, {base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - def test_to_string(self, mocker): + @pytest.mark.parametrize( + "query_parameter,default,required,expected", + [ + (False, None, False, "test: Union[Unset, TestType] = UNSET"), + (False, None, True, "test: TestType"), + (False, "Test", False, "test: Union[Unset, TestType] = Test"), + (False, "Test", True, "test: TestType = Test"), + (True, None, False, "test: Union[Unset, None, TestType] = UNSET"), + (True, None, True, "test: TestType"), + (True, "Test", False, "test: Union[Unset, None, TestType] = Test"), + (True, "Test", True, "test: TestType = Test"), + ], + ) + def test_to_string(self, mocker, query_parameter, default, required, expected): from openapi_python_client.parser.properties import Property name = "test" - get_type_string = mocker.patch.object(Property, "get_type_string") - p = Property(name=name, required=True, default=None, nullable=False) - - assert p.to_string() == f"{name}: {get_type_string()}" - - p = Property(name=name, required=False, default=None, nullable=False) - assert p.to_string() == f"{name}: {get_type_string()} = UNSET" - - p = Property(name=name, required=True, default=None, nullable=False) - assert p.to_string() == f"{name}: {get_type_string()}" + mocker.patch.object(Property, "_type_string", "TestType") + p = Property(name=name, required=required, default=default, nullable=False) - p = Property(name=name, required=True, default="TEST", nullable=False) - assert p.to_string() == f"{name}: {get_type_string()} = TEST" + assert p.to_string(query_parameter=query_parameter) == expected def test_get_imports(self): from openapi_python_client.parser.properties import Property @@ -87,7 +97,7 @@ def test_get_type_string(self): assert p.get_type_string() == f"Optional[{base_type_string}]" p = StringProperty(name="test", required=False, default=None, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" p = StringProperty(name="test", required=False, default=None, nullable=False) assert p.get_type_string() == f"Union[Unset, {base_type_string}]" @@ -202,7 +212,7 @@ def test_get_type_string(self, mocker): assert p.get_type_string(no_optional=True) == base_type_string p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=True) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" assert p.get_type_string(no_optional=True) == base_type_string p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=False) @@ -242,7 +252,28 @@ def test_get_type_imports(self, mocker): class TestUnionProperty: - def test_get_type_string(self, mocker): + @pytest.mark.parametrize( + "query_parameter,nullable,required,no_optional,expected", + [ + (False, False, False, False, "Union[Unset, inner_type_string_1, inner_type_string_2]"), + (False, False, False, True, "Union[inner_type_string_1, inner_type_string_2]"), + (False, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (False, False, True, True, "Union[inner_type_string_1, inner_type_string_2]"), + (False, True, False, False, "Union[Unset, None, inner_type_string_1, inner_type_string_2]"), + (False, True, False, True, "Union[inner_type_string_1, inner_type_string_2]"), + (False, True, True, False, "Union[None, inner_type_string_1, inner_type_string_2]"), + (False, True, True, True, "Union[inner_type_string_1, inner_type_string_2]"), + (True, False, False, False, "Union[Unset, None, inner_type_string_1, inner_type_string_2]"), + (True, False, False, True, "Union[inner_type_string_1, inner_type_string_2]"), + (True, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), + (True, False, True, True, "Union[inner_type_string_1, inner_type_string_2]"), + (True, True, False, False, "Union[Unset, None, inner_type_string_1, inner_type_string_2]"), + (True, True, False, True, "Union[inner_type_string_1, inner_type_string_2]"), + (True, True, True, False, "Union[None, inner_type_string_1, inner_type_string_2]"), + (True, True, True, True, "Union[inner_type_string_1, inner_type_string_2]"), + ], + ) + def test_get_type_string(self, mocker, query_parameter, nullable, required, no_optional, expected): from openapi_python_client.parser.properties import UnionProperty inner_property_1 = mocker.MagicMock() @@ -251,46 +282,13 @@ def test_get_type_string(self, mocker): inner_property_2.get_type_string.return_value = "inner_type_string_2" p = UnionProperty( name="test", - required=True, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=False, - ) - - base_type_string = f"Union[inner_type_string_1, inner_type_string_2]" - - assert p.get_type_string() == base_type_string - - p = UnionProperty( - name="test", - required=True, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=True, - ) - assert p.get_type_string() == f"Optional[{base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - base_type_string_with_unset = f"Union[Unset, inner_type_string_1, inner_type_string_2]" - p = UnionProperty( - name="test", - required=False, + required=required, default=None, inner_properties=[inner_property_1, inner_property_2], - nullable=True, + nullable=nullable, ) - assert p.get_type_string() == f"Optional[{base_type_string_with_unset}]" - assert p.get_type_string(no_optional=True) == base_type_string - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=False, - ) - assert p.get_type_string() == base_type_string_with_unset - assert p.get_type_string(no_optional=True) == base_type_string + assert p.get_type_string(query_parameter=query_parameter, no_optional=no_optional) == expected def test_get_imports(self, mocker): from openapi_python_client.parser.properties import UnionProperty @@ -389,7 +387,7 @@ def test_get_type_string(self, mocker): reference=fake_reference, value_type=str, ) - assert p.get_type_string() == f"Union[Unset, Optional[{base_type_string}]]" + assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" assert p.get_type_string(no_optional=True) == base_type_string p = properties.EnumProperty( @@ -586,7 +584,6 @@ def test_property_from_data_ref_model(self): nullable=False, default=None, reference=Reference(class_name=class_name, module_name="my_model"), - references=[], required_properties=[], optional_properties=[], description="", @@ -603,7 +600,6 @@ def test_property_from_data_ref_model(self): nullable=False, default=None, reference=Reference(class_name=class_name, module_name="my_model"), - references=[], required_properties=[], optional_properties=[], description="", @@ -990,25 +986,19 @@ def test__string_based_property_unsupported_format(self, mocker): def test_build_schemas(mocker): build_model_property = mocker.patch(f"{MODULE_NAME}.build_model_property") in_data = {"1": mocker.MagicMock(enum=None), "2": mocker.MagicMock(enum=None), "3": mocker.MagicMock(enum=None)} - model_1 = mocker.MagicMock() schemas_1 = mocker.MagicMock() model_2 = mocker.MagicMock() schemas_2 = mocker.MagicMock(errors=[]) - schemas_2.models = {"1": model_1, "2": model_2} - error_1 = PropertyError() + error = PropertyError() schemas_3 = mocker.MagicMock() - schemas_4 = mocker.MagicMock(errors=[]) - model_1.resolve_references.return_value = schemas_4 - error_2 = PropertyError() - model_2.resolve_references.return_value = error_2 # This loops through one for each, then again to retry the error build_model_property.side_effect = [ (model_1, schemas_1), (model_2, schemas_2), - (error_1, schemas_3), - (error_1, schemas_3), + (error, schemas_3), + (error, schemas_3), ] from openapi_python_client.parser.properties import Schemas, build_schemas @@ -1024,12 +1014,8 @@ def test_build_schemas(mocker): ] ) # schemas_3 was the last to come back from build_model_property, but it should be ignored because it's an error - model_1.resolve_references.assert_called_once_with(components=in_data, schemas=schemas_2) - # schemas_4 came from resolving model_1 - model_2.resolve_references.assert_called_once_with(components=in_data, schemas=schemas_4) - # resolving model_2 resulted in err, so no schemas_5 - assert result == schemas_4 - assert result.errors == [error_1, error_2] + assert result == schemas_2 + assert result.errors == [error] def test_build_parse_error_on_reference(): @@ -1103,7 +1089,6 @@ def test_build_model_property(additional_properties_schema, expected_additional_ nullable=False, default=None, reference=Reference(class_name="ParentMyModel", module_name="parent_my_model"), - references=[], required_properties=[StringProperty(name="req", required=True, nullable=False, default=None)], optional_properties=[DateTimeProperty(name="opt", required=False, nullable=False, default=None)], description=data.description, diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 421f40d48..410112666 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -4,9 +4,9 @@ @pytest.mark.parametrize( "no_optional,nullable,required,expected", [ - (False, False, False, "Union[MyClass, Unset]"), + (False, False, False, "Union[Unset, MyClass]"), (False, False, True, "MyClass"), - (False, True, False, "Union[Optional[MyClass], Unset]"), + (False, True, False, "Union[Unset, None, MyClass]"), (False, True, True, "Optional[MyClass]"), (True, False, False, "MyClass"), (True, False, True, "MyClass"), @@ -23,7 +23,6 @@ def test_get_type_string(no_optional, nullable, required, expected): nullable=nullable, default=None, reference=Reference(class_name="MyClass", module_name="my_module"), - references=[], description="", optional_properties=[], required_properties=[], @@ -43,7 +42,6 @@ def test_get_imports(): nullable=True, default=None, reference=Reference(class_name="MyClass", module_name="my_module"), - references=[], description="", optional_properties=[], required_properties=[], @@ -59,143 +57,3 @@ def test_get_imports(): "from typing import Dict", "from typing import cast", } - - -def test_resolve_references(mocker): - import openapi_python_client.schema as oai - from openapi_python_client.parser.properties import build_model_property - - schemas = { - "RefA": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["String"], - properties={ - "String": oai.Schema.construct(type="string"), - "Enum": oai.Schema.construct(type="string", enum=["aValue"]), - "DateTime": oai.Schema.construct(type="string", format="date-time"), - }, - ), - "RefB": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["DateTime"], - properties={ - "Int": oai.Schema.construct(type="integer"), - "DateTime": oai.Schema.construct(type="string", format="date-time"), - "Float": oai.Schema.construct(type="number", format="float"), - }, - ), - # Intentionally no properties defined - "RefC": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - ), - } - - model_schema = oai.Schema.construct( - allOf=[ - oai.Reference.construct(ref="#/components/schemas/RefA"), - oai.Reference.construct(ref="#/components/schemas/RefB"), - oai.Reference.construct(ref="#/components/schemas/RefC"), - oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["Float"], - properties={ - "String": oai.Schema.construct(type="string"), - "Float": oai.Schema.construct(type="number", format="float"), - }, - ), - ] - ) - - components = {**schemas, "Model": model_schema} - - from openapi_python_client.parser.properties import Schemas - - schemas_holder = Schemas() - model, schemas_holder = build_model_property( - data=model_schema, name="Model", required=True, schemas=schemas_holder, parent_name=None - ) - model.resolve_references(components, schemas_holder) - assert sorted(p.name for p in model.required_properties) == ["DateTime", "Float", "String"] - assert all(p.required for p in model.required_properties) - assert sorted(p.name for p in model.optional_properties) == ["Enum", "Int"] - assert all(not p.required for p in model.optional_properties) - - -def test_resolve_references_nested_allof(mocker): - import openapi_python_client.schema as oai - from openapi_python_client.parser.properties import build_model_property - - schemas = { - "RefA": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["String"], - properties={ - "String": oai.Schema.construct(type="string"), - "Enum": oai.Schema.construct(type="string", enum=["aValue"]), - "DateTime": oai.Schema.construct(type="string", format="date-time"), - }, - ), - "RefB": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["DateTime"], - properties={ - "Int": oai.Schema.construct(type="integer"), - "DateTime": oai.Schema.construct(type="string", format="date-time"), - "Float": oai.Schema.construct(type="number", format="float"), - }, - ), - # Intentionally no properties defined - "RefC": oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - ), - } - - model_schema = oai.Schema.construct( - type="object", - properties={ - "Key": oai.Schema.construct( - allOf=[ - oai.Reference.construct(ref="#/components/schemas/RefA"), - oai.Reference.construct(ref="#/components/schemas/RefB"), - oai.Reference.construct(ref="#/components/schemas/RefC"), - oai.Schema.construct( - title=mocker.MagicMock(), - description=mocker.MagicMock(), - required=["Float"], - properties={ - "String": oai.Schema.construct(type="string"), - "Float": oai.Schema.construct(type="number", format="float"), - }, - ), - ] - ), - }, - ) - - components = {**schemas, "Model": model_schema} - - from openapi_python_client.parser.properties import ModelProperty, Schemas - - schemas_holder = Schemas() - model, schemas_holder = build_model_property( - data=model_schema, name="Model", required=True, schemas=schemas_holder, parent_name=None - ) - model.resolve_references(components, schemas_holder) - assert sorted(p.name for p in model.required_properties) == [] - assert sorted(p.name for p in model.optional_properties) == ["Key"] - assert all(not p.required for p in model.optional_properties) - - key_property = model.optional_properties[0] - assert isinstance(key_property, ModelProperty) - key_property.resolve_references(components, schemas_holder) - assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"] - assert all(p.required for p in key_property.required_properties) - assert sorted(p.name for p in key_property.optional_properties) == ["Enum", "Int"] - assert all(not p.required for p in key_property.optional_properties) diff --git a/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py b/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py index cf8780024..be32cfbd3 100644 --- a/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py +++ b/tests/test_templates/test_property_templates/test_date_property/optional_nullable.py @@ -6,7 +6,7 @@ some_source = date(2020, 10, 12) -some_destination: Union[Unset, str] = UNSET +some_destination: Union[Unset, None, str] = UNSET if not isinstance(some_source, Unset): some_destination = some_source.isoformat() if some_source else None From a5d09f412a7520040efd763bb3d3c4b08f0d9975 Mon Sep 17 00:00:00 2001 From: Forest Tong Date: Wed, 27 Jan 2021 18:11:39 -0500 Subject: [PATCH 2/5] Regenerate tests --- .../api/tests/defaults_tests_defaults_post.py | 114 +++++---- ...tional_value_tests_optional_query_param.py | 11 +- .../custom_e2e/models/__init__.py | 4 - .../custom_e2e/models/a_model.py | 52 +--- .../custom_e2e/models/a_model_model.py | 85 ------- .../models/a_model_not_required_model.py | 85 ------- .../a_model_not_required_nullable_model.py | 85 ------- .../models/a_model_nullable_model.py | 85 ------- ...el_with_primitive_additional_properties.py | 6 +- .../models/model_with_union_property.py | 6 +- .../golden-record-custom/custom_e2e/types.py | 5 +- .../api/tests/defaults_tests_defaults_post.py | 232 ++++++++++-------- ...tional_value_tests_optional_query_param.py | 19 +- .../my_test_api_client/models/__init__.py | 4 - .../my_test_api_client/models/a_model.py | 52 +--- .../models/a_model_model.py | 85 ------- .../models/a_model_not_required_model.py | 85 ------- .../a_model_not_required_nullable_model.py | 85 ------- .../models/a_model_nullable_model.py | 85 ------- ...el_with_primitive_additional_properties.py | 6 +- .../models/model_with_union_property.py | 6 +- .../golden-record/my_test_api_client/types.py | 5 +- 22 files changed, 243 insertions(+), 959 deletions(-) delete mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py delete mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py delete mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py delete mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py delete mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py delete mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py delete mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py delete mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py index f87ad3e1b..4a2c9074b 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py @@ -7,13 +7,12 @@ Client = httpx.Client import datetime -from typing import Dict, List, Union +from typing import Dict, List, Optional, Union from dateutil.parser import isoparse from ...models.an_enum import AnEnum from ...models.http_validation_error import HTTPValidationError -from ...models.model_with_union_property import ModelWithUnionProperty from ...types import UNSET, Unset @@ -41,83 +40,106 @@ def _build_response(*, response: httpx.Response) -> Response[Union[None, HTTPVal def httpx_request( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: - json_datetime_prop: Union[Unset, str] = UNSET - if not isinstance(datetime_prop, Unset): - json_datetime_prop = datetime_prop.isoformat() + json_not_required_not_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_not_nullable_datetime_prop, Unset): + json_not_required_not_nullable_datetime_prop = ( + not_required_not_nullable_datetime_prop.isoformat() if not_required_not_nullable_datetime_prop else None + ) - json_date_prop: Union[Unset, str] = UNSET + json_not_required_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_nullable_datetime_prop, Unset): + json_not_required_nullable_datetime_prop = ( + not_required_nullable_datetime_prop.isoformat() if not_required_nullable_datetime_prop else None + ) + + json_required_not_nullable_datetime_prop = required_not_nullable_datetime_prop.isoformat() + + json_required_nullable_datetime_prop = ( + required_nullable_datetime_prop.isoformat() if required_nullable_datetime_prop else None + ) + + json_date_prop: Union[Unset, None, str] = UNSET if not isinstance(date_prop, Unset): - json_date_prop = date_prop.isoformat() + json_date_prop = date_prop.isoformat() if date_prop else None - json_list_prop: Union[Unset, List[Any]] = UNSET + json_list_prop: Union[Unset, None, List[Any]] = UNSET if not isinstance(list_prop, Unset): - json_list_prop = [] - for list_prop_item_data in list_prop: - list_prop_item = list_prop_item_data.value + if list_prop is None: + json_list_prop = None + else: + json_list_prop = [] + for list_prop_item_data in list_prop: + list_prop_item = list_prop_item_data.value - json_list_prop.append(list_prop_item) + json_list_prop.append(list_prop_item) - json_union_prop: Union[Unset, float, str] + json_union_prop: Union[Unset, None, float, str] if isinstance(union_prop, Unset): json_union_prop = UNSET + elif union_prop is None: + json_union_prop = None else: json_union_prop = union_prop - json_union_prop_with_ref: Union[Unset, float, AnEnum] + json_union_prop_with_ref: Union[Unset, None, float, int] if isinstance(union_prop_with_ref, Unset): json_union_prop_with_ref = UNSET + elif union_prop_with_ref is None: + json_union_prop_with_ref = None elif isinstance(union_prop_with_ref, AnEnum): json_union_prop_with_ref = UNSET if not isinstance(union_prop_with_ref, Unset): - json_union_prop_with_ref = union_prop_with_ref + json_union_prop_with_ref = union_prop_with_ref.value else: json_union_prop_with_ref = union_prop_with_ref - json_enum_prop: Union[Unset, AnEnum] = UNSET + json_enum_prop: Union[Unset, None, int] = UNSET if not isinstance(enum_prop, Unset): - json_enum_prop = enum_prop - - json_model_prop: Union[Unset, Dict[str, Any]] = UNSET - if not isinstance(model_prop, Unset): - json_model_prop = model_prop.to_dict() + json_enum_prop = enum_prop.value if enum_prop else None - params: Dict[str, Any] = {} - if not isinstance(string_prop, Unset) and string_prop is not None: + params: Dict[str, Any] = { + "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, + } + if string_prop is not UNSET and string_prop is not None: params["string_prop"] = string_prop - if not isinstance(json_datetime_prop, Unset) and json_datetime_prop is not None: - params["datetime_prop"] = json_datetime_prop - if not isinstance(json_date_prop, Unset) and json_date_prop is not None: + if not_required_not_nullable_datetime_prop is not UNSET and not_required_not_nullable_datetime_prop is not None: + params["not_required_not_nullable_datetime_prop"] = json_not_required_not_nullable_datetime_prop + if not_required_nullable_datetime_prop is not UNSET and not_required_nullable_datetime_prop is not None: + params["not_required_nullable_datetime_prop"] = json_not_required_nullable_datetime_prop + if required_nullable_datetime_prop is not None: + params["required_nullable_datetime_prop"] = json_required_nullable_datetime_prop + if date_prop is not UNSET and date_prop is not None: params["date_prop"] = json_date_prop - if not isinstance(float_prop, Unset) and float_prop is not None: + if float_prop is not UNSET and float_prop is not None: params["float_prop"] = float_prop - if not isinstance(int_prop, Unset) and int_prop is not None: + if int_prop is not UNSET and int_prop is not None: params["int_prop"] = int_prop - if not isinstance(boolean_prop, Unset) and boolean_prop is not None: + if boolean_prop is not UNSET and boolean_prop is not None: params["boolean_prop"] = boolean_prop - if not isinstance(json_list_prop, Unset) and json_list_prop is not None: + if list_prop is not UNSET and list_prop is not None: params["list_prop"] = json_list_prop - if not isinstance(json_union_prop, Unset) and json_union_prop is not None: + if union_prop is not UNSET and union_prop is not None: params["union_prop"] = json_union_prop - if not isinstance(json_union_prop_with_ref, Unset) and json_union_prop_with_ref is not None: + if union_prop_with_ref is not UNSET and union_prop_with_ref is not None: params["union_prop_with_ref"] = json_union_prop_with_ref - if not isinstance(json_enum_prop, Unset) and json_enum_prop is not None: + if enum_prop is not UNSET and enum_prop is not None: params["enum_prop"] = json_enum_prop - if not isinstance(json_model_prop, Unset) and json_model_prop is not None: - params.update(json_model_prop) response = client.request( "post", diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py index 616dc4252..8ff5a52a3 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py @@ -36,15 +36,18 @@ def _build_response(*, response: httpx.Response) -> Response[Union[None, HTTPVal def httpx_request( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: - json_query_param: Union[Unset, List[Any]] = UNSET + json_query_param: Union[Unset, None, List[Any]] = UNSET if not isinstance(query_param, Unset): - json_query_param = query_param + if query_param is None: + json_query_param = None + else: + json_query_param = query_param params: Dict[str, Any] = {} - if not isinstance(json_query_param, Unset) and json_query_param is not None: + if query_param is not UNSET and query_param is not None: params["query_param"] = json_query_param response = client.request( diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py index 6f5ac7423..d3ca924b3 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py @@ -1,10 +1,6 @@ """ Contains all the data models used in inputs/outputs """ from .a_model import AModel -from .a_model_model import AModelModel -from .a_model_not_required_model import AModelNotRequiredModel -from .a_model_not_required_nullable_model import AModelNotRequiredNullableModel -from .a_model_nullable_model import AModelNullableModel from .an_enum import AnEnum from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py index 15a7cc68c..f811d4712 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py @@ -4,10 +4,6 @@ import attr from dateutil.parser import isoparse -from ..models.a_model_model import AModelModel -from ..models.a_model_not_required_model import AModelNotRequiredModel -from ..models.a_model_not_required_nullable_model import AModelNotRequiredNullableModel -from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum from ..types import UNSET, Unset @@ -21,16 +17,12 @@ class AModel: a_camel_date_time: Union[datetime.datetime, datetime.date] a_date: datetime.date required_not_nullable: str - model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] - nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET - not_required_nullable: Union[Unset, Optional[str]] = UNSET + not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - not_required_nullable_model: Union[Optional[AModelNotRequiredNullableModel], Unset] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -43,8 +35,6 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable - model = self.model.to_dict() - nested_list_of_enums: Union[Unset, List[Any]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] @@ -62,17 +52,6 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable - nullable_model = self.nullable_model.to_dict() if self.nullable_model else None - - not_required_model: Union[Unset, Dict[str, Any]] = UNSET - if not isinstance(self.not_required_model, Unset): - not_required_model = self.not_required_model.to_dict() - - not_required_nullable_model: Union[None, Unset, Dict[str, Any]] = UNSET - if not isinstance(self.not_required_nullable_model, Unset): - not_required_nullable_model = ( - self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None - ) field_dict: Dict[str, Any] = {} field_dict.update( @@ -81,10 +60,8 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, - "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, - "nullable_model": nullable_model, } ) if nested_list_of_enums is not UNSET: @@ -95,10 +72,6 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable - if not_required_model is not UNSET: - field_dict["not_required_model"] = not_required_model - if not_required_nullable_model is not UNSET: - field_dict["not_required_nullable_model"] = not_required_nullable_model return field_dict @@ -126,8 +99,6 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") - model = AModelModel.from_dict(d.pop("model")) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) for nested_list_of_enums_item_data in _nested_list_of_enums or []: @@ -153,38 +124,17 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - nullable_model = None - _nullable_model = d.pop("nullable_model") - if _nullable_model is not None: - nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model)) - - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - _not_required_model = d.pop("not_required_model", UNSET) - if not isinstance(_not_required_model, Unset): - not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) - - not_required_nullable_model = None - _not_required_nullable_model = d.pop("not_required_nullable_model", UNSET) - if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset): - not_required_nullable_model = AModelNotRequiredNullableModel.from_dict( - cast(Dict[str, Any], _not_required_nullable_model) - ) - a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, - model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, attr_1_leading_digit=attr_1_leading_digit, required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, - nullable_model=nullable_model, - not_required_model=not_required_model, - not_required_nullable_model=not_required_nullable_model, ) return a_model diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py deleted file mode 100644 index c1a00c152..000000000 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_model = AModelModel( - a_property=a_property, - ) - - a_model_model.additional_properties = d - return a_model_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py deleted file mode 100644 index adecb4225..000000000 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNotRequiredModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_not_required_model = AModelNotRequiredModel( - a_property=a_property, - ) - - a_model_not_required_model.additional_properties = d - return a_model_not_required_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py deleted file mode 100644 index 9de2e3798..000000000 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNotRequiredNullableModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredNullableModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_not_required_nullable_model = AModelNotRequiredNullableModel( - a_property=a_property, - ) - - a_model_not_required_nullable_model.additional_properties = d - return a_model_not_required_nullable_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py deleted file mode 100644 index cbcf120f8..000000000 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNullableModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNullableModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_nullable_model = AModelNullableModel( - a_property=a_property, - ) - - a_model_nullable_model.additional_properties = d - return a_model_nullable_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py index 797c1ce31..feb30878d 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py @@ -12,7 +12,7 @@ class ModelWithPrimitiveAdditionalProperties: """ """ - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) def to_dict(self) -> Dict[str, Any]: @@ -31,9 +31,9 @@ def to_dict(self) -> Dict[str, Any]: @staticmethod def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperties": d = src_dict.copy() - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) - if not isinstance(_a_date_holder, Unset): + if _a_date_holder is not None and not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( cast(Dict[str, Any], _a_date_holder) ) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py index cbecc7c90..35bc2a853 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_union_property.py @@ -14,18 +14,18 @@ class ModelWithUnionProperty: a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] + a_property: Union[Unset, int] if isinstance(self.a_property, Unset): a_property = UNSET elif isinstance(self.a_property, AnEnum): a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value else: a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value field_dict: Dict[str, Any] = {} field_dict.update({}) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/types.py b/end_to_end_tests/golden-record-custom/custom_e2e/types.py index 2061b9f08..a354a2192 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/types.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/types.py @@ -11,6 +11,9 @@ def __bool__(self) -> bool: UNSET: Unset = Unset() +# Used as `FileProperty._json_type_string` +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +23,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py index ff6ef2409..1f865570d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py @@ -7,93 +7,115 @@ from ...client import Client from ...models.an_enum import AnEnum from ...models.http_validation_error import HTTPValidationError -from ...models.model_with_union_property import ModelWithUnionProperty from ...types import UNSET, Response, Unset def _get_kwargs( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Dict[str, Any]: url = "{}/tests/defaults".format(client.base_url) headers: Dict[str, Any] = client.get_headers() - json_datetime_prop: Union[Unset, str] = UNSET - if not isinstance(datetime_prop, Unset): - json_datetime_prop = datetime_prop.isoformat() + json_not_required_not_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_not_nullable_datetime_prop, Unset): + json_not_required_not_nullable_datetime_prop = ( + not_required_not_nullable_datetime_prop.isoformat() if not_required_not_nullable_datetime_prop else None + ) + + json_not_required_nullable_datetime_prop: Union[Unset, None, str] = UNSET + if not isinstance(not_required_nullable_datetime_prop, Unset): + json_not_required_nullable_datetime_prop = ( + not_required_nullable_datetime_prop.isoformat() if not_required_nullable_datetime_prop else None + ) + + json_required_not_nullable_datetime_prop = required_not_nullable_datetime_prop.isoformat() - json_date_prop: Union[Unset, str] = UNSET + json_required_nullable_datetime_prop = ( + required_nullable_datetime_prop.isoformat() if required_nullable_datetime_prop else None + ) + + json_date_prop: Union[Unset, None, str] = UNSET if not isinstance(date_prop, Unset): - json_date_prop = date_prop.isoformat() + json_date_prop = date_prop.isoformat() if date_prop else None - json_list_prop: Union[Unset, List[Any]] = UNSET + json_list_prop: Union[Unset, None, List[Any]] = UNSET if not isinstance(list_prop, Unset): - json_list_prop = [] - for list_prop_item_data in list_prop: - list_prop_item = list_prop_item_data.value + if list_prop is None: + json_list_prop = None + else: + json_list_prop = [] + for list_prop_item_data in list_prop: + list_prop_item = list_prop_item_data.value - json_list_prop.append(list_prop_item) + json_list_prop.append(list_prop_item) - json_union_prop: Union[Unset, float, str] + json_union_prop: Union[Unset, None, float, str] if isinstance(union_prop, Unset): json_union_prop = UNSET + elif union_prop is None: + json_union_prop = None else: json_union_prop = union_prop - json_union_prop_with_ref: Union[Unset, float, AnEnum] + json_union_prop_with_ref: Union[Unset, None, float, int] if isinstance(union_prop_with_ref, Unset): json_union_prop_with_ref = UNSET + elif union_prop_with_ref is None: + json_union_prop_with_ref = None elif isinstance(union_prop_with_ref, AnEnum): json_union_prop_with_ref = UNSET if not isinstance(union_prop_with_ref, Unset): - json_union_prop_with_ref = union_prop_with_ref + json_union_prop_with_ref = union_prop_with_ref.value else: json_union_prop_with_ref = union_prop_with_ref - json_enum_prop: Union[Unset, AnEnum] = UNSET + json_enum_prop: Union[Unset, None, int] = UNSET if not isinstance(enum_prop, Unset): - json_enum_prop = enum_prop + json_enum_prop = enum_prop.value if enum_prop else None - json_model_prop: Union[Unset, Dict[str, Any]] = UNSET - if not isinstance(model_prop, Unset): - json_model_prop = model_prop.to_dict() - - params: Dict[str, Any] = {} - if not isinstance(string_prop, Unset) and string_prop is not None: + params: Dict[str, Any] = { + "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, + } + if string_prop is not UNSET and string_prop is not None: params["string_prop"] = string_prop - if not isinstance(json_datetime_prop, Unset) and json_datetime_prop is not None: - params["datetime_prop"] = json_datetime_prop - if not isinstance(json_date_prop, Unset) and json_date_prop is not None: + if not_required_not_nullable_datetime_prop is not UNSET and not_required_not_nullable_datetime_prop is not None: + params["not_required_not_nullable_datetime_prop"] = json_not_required_not_nullable_datetime_prop + if not_required_nullable_datetime_prop is not UNSET and not_required_nullable_datetime_prop is not None: + params["not_required_nullable_datetime_prop"] = json_not_required_nullable_datetime_prop + if required_nullable_datetime_prop is not None: + params["required_nullable_datetime_prop"] = json_required_nullable_datetime_prop + if date_prop is not UNSET and date_prop is not None: params["date_prop"] = json_date_prop - if not isinstance(float_prop, Unset) and float_prop is not None: + if float_prop is not UNSET and float_prop is not None: params["float_prop"] = float_prop - if not isinstance(int_prop, Unset) and int_prop is not None: + if int_prop is not UNSET and int_prop is not None: params["int_prop"] = int_prop - if not isinstance(boolean_prop, Unset) and boolean_prop is not None: + if boolean_prop is not UNSET and boolean_prop is not None: params["boolean_prop"] = boolean_prop - if not isinstance(json_list_prop, Unset) and json_list_prop is not None: + if list_prop is not UNSET and list_prop is not None: params["list_prop"] = json_list_prop - if not isinstance(json_union_prop, Unset) and json_union_prop is not None: + if union_prop is not UNSET and union_prop is not None: params["union_prop"] = json_union_prop - if not isinstance(json_union_prop_with_ref, Unset) and json_union_prop_with_ref is not None: + if union_prop_with_ref is not UNSET and union_prop_with_ref is not None: params["union_prop_with_ref"] = json_union_prop_with_ref - if not isinstance(json_enum_prop, Unset) and json_enum_prop is not None: + if enum_prop is not UNSET and enum_prop is not None: params["enum_prop"] = json_enum_prop - if not isinstance(json_model_prop, Unset) and json_model_prop is not None: - params.update(json_model_prop) return { "url": url, @@ -128,22 +150,27 @@ def _build_response(*, response: httpx.Response) -> Response[Union[None, HTTPVal def sync_detailed( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -152,7 +179,6 @@ def sync_detailed( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, - model_prop=model_prop, ) response = httpx.post( @@ -165,24 +191,29 @@ def sync_detailed( def sync( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ """ return sync_detailed( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -191,29 +222,33 @@ def sync( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, - model_prop=model_prop, ).parsed async def asyncio_detailed( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -222,7 +257,6 @@ async def asyncio_detailed( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, - model_prop=model_prop, ) async with httpx.AsyncClient() as _client: @@ -234,17 +268,19 @@ async def asyncio_detailed( async def asyncio( *, client: Client, - string_prop: Union[Unset, str] = "the default string", - datetime_prop: Union[Unset, datetime.datetime] = isoparse("1010-10-10T00:00:00"), - date_prop: Union[Unset, datetime.date] = isoparse("1010-10-10").date(), - float_prop: Union[Unset, float] = 3.14, - int_prop: Union[Unset, int] = 7, - boolean_prop: Union[Unset, bool] = False, - list_prop: Union[Unset, List[AnEnum]] = UNSET, - union_prop: Union[Unset, float, str] = "not a float", - union_prop_with_ref: Union[Unset, float, AnEnum] = 0.6, - enum_prop: Union[Unset, AnEnum] = UNSET, - model_prop: Union[ModelWithUnionProperty, Unset] = UNSET, + string_prop: Union[Unset, None, str] = "the default string", + not_required_not_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + not_required_nullable_datetime_prop: Union[Unset, None, datetime.datetime] = isoparse("1010-10-10T00:00:00"), + required_not_nullable_datetime_prop: datetime.datetime = isoparse("1010-10-10T00:00:00"), + required_nullable_datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"), + date_prop: Union[Unset, None, datetime.date] = isoparse("1010-10-10").date(), + float_prop: Union[Unset, None, float] = 3.14, + int_prop: Union[Unset, None, int] = 7, + boolean_prop: Union[Unset, None, bool] = False, + list_prop: Union[Unset, None, List[AnEnum]] = UNSET, + union_prop: Union[Unset, None, float, str] = "not a float", + union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, + enum_prop: Union[Unset, None, AnEnum] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ """ @@ -252,7 +288,10 @@ async def asyncio( await asyncio_detailed( client=client, string_prop=string_prop, - datetime_prop=datetime_prop, + not_required_not_nullable_datetime_prop=not_required_not_nullable_datetime_prop, + not_required_nullable_datetime_prop=not_required_nullable_datetime_prop, + required_not_nullable_datetime_prop=required_not_nullable_datetime_prop, + required_nullable_datetime_prop=required_nullable_datetime_prop, date_prop=date_prop, float_prop=float_prop, int_prop=int_prop, @@ -261,6 +300,5 @@ async def asyncio( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, - model_prop=model_prop, ) ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py index 576a770fe..2c14c2949 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py @@ -10,18 +10,21 @@ def _get_kwargs( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Dict[str, Any]: url = "{}/tests/optional_query_param/".format(client.base_url) headers: Dict[str, Any] = client.get_headers() - json_query_param: Union[Unset, List[Any]] = UNSET + json_query_param: Union[Unset, None, List[Any]] = UNSET if not isinstance(query_param, Unset): - json_query_param = query_param + if query_param is None: + json_query_param = None + else: + json_query_param = query_param params: Dict[str, Any] = {} - if not isinstance(json_query_param, Unset) and json_query_param is not None: + if query_param is not UNSET and query_param is not None: params["query_param"] = json_query_param return { @@ -57,7 +60,7 @@ def _build_response(*, response: httpx.Response) -> Response[Union[None, HTTPVal def sync_detailed( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, @@ -74,7 +77,7 @@ def sync_detailed( def sync( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ Test optional query parameters """ @@ -87,7 +90,7 @@ def sync( async def asyncio_detailed( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, @@ -103,7 +106,7 @@ async def asyncio_detailed( async def asyncio( *, client: Client, - query_param: Union[Unset, List[str]] = UNSET, + query_param: Union[Unset, None, List[str]] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ Test optional query parameters """ diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index 6f5ac7423..d3ca924b3 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -1,10 +1,6 @@ """ Contains all the data models used in inputs/outputs """ from .a_model import AModel -from .a_model_model import AModelModel -from .a_model_not_required_model import AModelNotRequiredModel -from .a_model_not_required_nullable_model import AModelNotRequiredNullableModel -from .a_model_nullable_model import AModelNullableModel from .an_enum import AnEnum from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index 15a7cc68c..f811d4712 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py @@ -4,10 +4,6 @@ import attr from dateutil.parser import isoparse -from ..models.a_model_model import AModelModel -from ..models.a_model_not_required_model import AModelNotRequiredModel -from ..models.a_model_not_required_nullable_model import AModelNotRequiredNullableModel -from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum from ..types import UNSET, Unset @@ -21,16 +17,12 @@ class AModel: a_camel_date_time: Union[datetime.datetime, datetime.date] a_date: datetime.date required_not_nullable: str - model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] - nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET - not_required_nullable: Union[Unset, Optional[str]] = UNSET + not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - not_required_nullable_model: Union[Optional[AModelNotRequiredNullableModel], Unset] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -43,8 +35,6 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable - model = self.model.to_dict() - nested_list_of_enums: Union[Unset, List[Any]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] @@ -62,17 +52,6 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable - nullable_model = self.nullable_model.to_dict() if self.nullable_model else None - - not_required_model: Union[Unset, Dict[str, Any]] = UNSET - if not isinstance(self.not_required_model, Unset): - not_required_model = self.not_required_model.to_dict() - - not_required_nullable_model: Union[None, Unset, Dict[str, Any]] = UNSET - if not isinstance(self.not_required_nullable_model, Unset): - not_required_nullable_model = ( - self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None - ) field_dict: Dict[str, Any] = {} field_dict.update( @@ -81,10 +60,8 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, - "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, - "nullable_model": nullable_model, } ) if nested_list_of_enums is not UNSET: @@ -95,10 +72,6 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable - if not_required_model is not UNSET: - field_dict["not_required_model"] = not_required_model - if not_required_nullable_model is not UNSET: - field_dict["not_required_nullable_model"] = not_required_nullable_model return field_dict @@ -126,8 +99,6 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") - model = AModelModel.from_dict(d.pop("model")) - nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) for nested_list_of_enums_item_data in _nested_list_of_enums or []: @@ -153,38 +124,17 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - nullable_model = None - _nullable_model = d.pop("nullable_model") - if _nullable_model is not None: - nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model)) - - not_required_model: Union[AModelNotRequiredModel, Unset] = UNSET - _not_required_model = d.pop("not_required_model", UNSET) - if not isinstance(_not_required_model, Unset): - not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) - - not_required_nullable_model = None - _not_required_nullable_model = d.pop("not_required_nullable_model", UNSET) - if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset): - not_required_nullable_model = AModelNotRequiredNullableModel.from_dict( - cast(Dict[str, Any], _not_required_nullable_model) - ) - a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, - model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, attr_1_leading_digit=attr_1_leading_digit, required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, - nullable_model=nullable_model, - not_required_model=not_required_model, - not_required_nullable_model=not_required_nullable_model, ) return a_model diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py deleted file mode 100644 index c1a00c152..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_model = AModelModel( - a_property=a_property, - ) - - a_model_model.additional_properties = d - return a_model_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py deleted file mode 100644 index adecb4225..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNotRequiredModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_not_required_model = AModelNotRequiredModel( - a_property=a_property, - ) - - a_model_not_required_model.additional_properties = d - return a_model_not_required_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py deleted file mode 100644 index 9de2e3798..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNotRequiredNullableModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredNullableModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_not_required_nullable_model = AModelNotRequiredNullableModel( - a_property=a_property, - ) - - a_model_not_required_nullable_model.additional_properties = d - return a_model_not_required_nullable_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py deleted file mode 100644 index cbcf120f8..000000000 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py +++ /dev/null @@ -1,85 +0,0 @@ -from typing import Any, Dict, List, Union - -import attr - -from ..models.an_enum import AnEnum -from ..models.an_int_enum import AnIntEnum -from ..types import UNSET, Unset - - -@attr.s(auto_attribs=True) -class AModelNullableModel: - """ """ - - a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET - additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) - - def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] - if isinstance(self.a_property, Unset): - a_property = UNSET - elif isinstance(self.a_property, AnEnum): - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - else: - a_property = UNSET - if not isinstance(self.a_property, Unset): - a_property = self.a_property - - field_dict: Dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({}) - if a_property is not UNSET: - field_dict["a_property"] = a_property - - return field_dict - - @staticmethod - def from_dict(src_dict: Dict[str, Any]) -> "AModelNullableModel": - d = src_dict.copy() - - def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: - data = None if isinstance(data, Unset) else data - a_property: Union[Unset, AnEnum, AnIntEnum] - try: - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnEnum(_a_property) - - return a_property - except: # noqa: E722 - pass - a_property = UNSET - _a_property = data - if _a_property is not None: - a_property = AnIntEnum(_a_property) - - return a_property - - a_property = _parse_a_property(d.pop("a_property", UNSET)) - - a_model_nullable_model = AModelNullableModel( - a_property=a_property, - ) - - a_model_nullable_model.additional_properties = d - return a_model_nullable_model - - @property - def additional_keys(self) -> List[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py index 797c1ce31..feb30878d 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py @@ -12,7 +12,7 @@ class ModelWithPrimitiveAdditionalProperties: """ """ - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET additional_properties: Dict[str, str] = attr.ib(init=False, factory=dict) def to_dict(self) -> Dict[str, Any]: @@ -31,9 +31,9 @@ def to_dict(self) -> Dict[str, Any]: @staticmethod def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperties": d = src_dict.copy() - a_date_holder: Union[ModelWithPrimitiveAdditionalPropertiesADateHolder, Unset] = UNSET + a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) - if not isinstance(_a_date_holder, Unset): + if _a_date_holder is not None and not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( cast(Dict[str, Any], _a_date_holder) ) diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py index cbecc7c90..35bc2a853 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property.py @@ -14,18 +14,18 @@ class ModelWithUnionProperty: a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET def to_dict(self) -> Dict[str, Any]: - a_property: Union[Unset, AnEnum, AnIntEnum] + a_property: Union[Unset, int] if isinstance(self.a_property, Unset): a_property = UNSET elif isinstance(self.a_property, AnEnum): a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value else: a_property = UNSET if not isinstance(self.a_property, Unset): - a_property = self.a_property + a_property = self.a_property.value field_dict: Dict[str, Any] = {} field_dict.update({}) diff --git a/end_to_end_tests/golden-record/my_test_api_client/types.py b/end_to_end_tests/golden-record/my_test_api_client/types.py index 2061b9f08..a354a2192 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/types.py +++ b/end_to_end_tests/golden-record/my_test_api_client/types.py @@ -11,6 +11,9 @@ def __bool__(self) -> bool: UNSET: Unset = Unset() +# Used as `FileProperty._json_type_string` +FileJsonType = Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]] + @attr.s(auto_attribs=True) class File: @@ -20,7 +23,7 @@ class File: file_name: Optional[str] = None mime_type: Optional[str] = None - def to_tuple(self) -> Tuple[Optional[str], Union[BinaryIO, TextIO], Optional[str]]: + def to_tuple(self) -> FileJsonType: """ Return a tuple representation that httpx will accept for multipart/form-data """ return self.file_name, self.payload, self.mime_type From b33df4d67af350dc710bd9aa59f35561b7e5c4a9 Mon Sep 17 00:00:00 2001 From: Forest Tong Date: Wed, 27 Jan 2021 18:19:49 -0500 Subject: [PATCH 3/5] Undo diffs --- .../api/tests/defaults_tests_defaults_post.py | 8 ++ .../custom_e2e/models/__init__.py | 4 + .../custom_e2e/models/a_model.py | 47 ++++++++++ .../custom_e2e/models/a_model_model.py | 85 +++++++++++++++++++ .../models/a_model_not_required_model.py | 85 +++++++++++++++++++ .../a_model_not_required_nullable_model.py | 85 +++++++++++++++++++ .../models/a_model_nullable_model.py | 85 +++++++++++++++++++ .../api/tests/defaults_tests_defaults_post.py | 16 ++++ .../my_test_api_client/models/__init__.py | 4 + .../my_test_api_client/models/a_model.py | 47 ++++++++++ .../models/a_model_model.py | 85 +++++++++++++++++++ .../models/a_model_not_required_model.py | 85 +++++++++++++++++++ .../a_model_not_required_nullable_model.py | 85 +++++++++++++++++++ .../models/a_model_nullable_model.py | 85 +++++++++++++++++++ end_to_end_tests/openapi.json | 46 +++++++++- .../parser/properties/__init__.py | 32 +++++-- .../parser/properties/model_property.py | 55 +++++++++++- .../parser/properties/property.py | 1 + pyproject.toml | 13 ++- 19 files changed, 942 insertions(+), 11 deletions(-) create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py create mode 100644 end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py index 4a2c9074b..e4b71e321 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py @@ -13,6 +13,7 @@ from ...models.an_enum import AnEnum from ...models.http_validation_error import HTTPValidationError +from ...models.model_with_union_property import ModelWithUnionProperty from ...types import UNSET, Unset @@ -53,6 +54,7 @@ def httpx_request( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: json_not_required_not_nullable_datetime_prop: Union[Unset, None, str] = UNSET @@ -113,6 +115,10 @@ def httpx_request( if not isinstance(enum_prop, Unset): json_enum_prop = enum_prop.value if enum_prop else None + json_model_prop: Union[Unset, None, Dict[str, Any]] = UNSET + if not isinstance(model_prop, Unset): + json_model_prop = model_prop.to_dict() if model_prop else None + params: Dict[str, Any] = { "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, } @@ -140,6 +146,8 @@ def httpx_request( params["union_prop_with_ref"] = json_union_prop_with_ref if enum_prop is not UNSET and enum_prop is not None: params["enum_prop"] = json_enum_prop + if model_prop is not UNSET and model_prop is not None: + params["model_prop"] = json_model_prop response = client.request( "post", diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py index d3ca924b3..6f5ac7423 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/__init__.py @@ -1,6 +1,10 @@ """ Contains all the data models used in inputs/outputs """ from .a_model import AModel +from .a_model_model import AModelModel +from .a_model_not_required_model import AModelNotRequiredModel +from .a_model_not_required_nullable_model import AModelNotRequiredNullableModel +from .a_model_nullable_model import AModelNullableModel from .an_enum import AnEnum from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py index f811d4712..3e46992a4 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py @@ -4,6 +4,10 @@ import attr from dateutil.parser import isoparse +from ..models.a_model_model import AModelModel +from ..models.a_model_not_required_model import AModelNotRequiredModel +from ..models.a_model_not_required_nullable_model import AModelNotRequiredNullableModel +from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum from ..types import UNSET, Unset @@ -17,12 +21,16 @@ class AModel: a_camel_date_time: Union[datetime.datetime, datetime.date] a_date: datetime.date required_not_nullable: str + model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] + nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + not_required_nullable_model: Union[Unset, None, AModelNotRequiredNullableModel] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -35,6 +43,8 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable + model = self.model.to_dict() + nested_list_of_enums: Union[Unset, List[Any]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] @@ -52,6 +62,17 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable + nullable_model = self.nullable_model.to_dict() if self.nullable_model else None + + not_required_model: Union[Unset, Dict[str, Any]] = UNSET + if not isinstance(self.not_required_model, Unset): + not_required_model = self.not_required_model.to_dict() + + not_required_nullable_model: Union[Unset, None, Dict[str, Any]] = UNSET + if not isinstance(self.not_required_nullable_model, Unset): + not_required_nullable_model = ( + self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None + ) field_dict: Dict[str, Any] = {} field_dict.update( @@ -60,8 +81,10 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, + "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, + "nullable_model": nullable_model, } ) if nested_list_of_enums is not UNSET: @@ -72,6 +95,10 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable + if not_required_model is not UNSET: + field_dict["not_required_model"] = not_required_model + if not_required_nullable_model is not UNSET: + field_dict["not_required_nullable_model"] = not_required_nullable_model return field_dict @@ -99,6 +126,8 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") + model = AModelModel.from_dict(d.pop("model")) + nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) for nested_list_of_enums_item_data in _nested_list_of_enums or []: @@ -124,17 +153,35 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) + nullable_model = AModelNullableModel.from_dict(d.pop("nullable_model")) + + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + _not_required_model = d.pop("not_required_model", UNSET) + if _not_required_model is not None and not isinstance(_not_required_model, Unset): + not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) + + not_required_nullable_model = None + _not_required_nullable_model = d.pop("not_required_nullable_model", UNSET) + if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset): + not_required_nullable_model = AModelNotRequiredNullableModel.from_dict( + cast(Dict[str, Any], _not_required_nullable_model) + ) + a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, + model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, attr_1_leading_digit=attr_1_leading_digit, required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, + nullable_model=nullable_model, + not_required_model=not_required_model, + not_required_nullable_model=not_required_nullable_model, ) return a_model diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py new file mode 100644 index 000000000..0700798c8 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_model = AModelModel( + a_property=a_property, + ) + + a_model_model.additional_properties = d + return a_model_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py new file mode 100644 index 000000000..a08f547aa --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNotRequiredModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_not_required_model = AModelNotRequiredModel( + a_property=a_property, + ) + + a_model_not_required_model.additional_properties = d + return a_model_not_required_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py new file mode 100644 index 000000000..52ee2ddc3 --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_not_required_nullable_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNotRequiredNullableModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredNullableModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_not_required_nullable_model = AModelNotRequiredNullableModel( + a_property=a_property, + ) + + a_model_not_required_nullable_model.additional_properties = d + return a_model_not_required_nullable_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py new file mode 100644 index 000000000..2f21cc5ac --- /dev/null +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model_nullable_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNullableModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNullableModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_nullable_model = AModelNullableModel( + a_property=a_property, + ) + + a_model_nullable_model.additional_properties = d + return a_model_nullable_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py index 1f865570d..540f68b44 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py @@ -7,6 +7,7 @@ from ...client import Client from ...models.an_enum import AnEnum from ...models.http_validation_error import HTTPValidationError +from ...models.model_with_union_property import ModelWithUnionProperty from ...types import UNSET, Response, Unset @@ -26,6 +27,7 @@ def _get_kwargs( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Dict[str, Any]: url = "{}/tests/defaults".format(client.base_url) @@ -89,6 +91,10 @@ def _get_kwargs( if not isinstance(enum_prop, Unset): json_enum_prop = enum_prop.value if enum_prop else None + json_model_prop: Union[Unset, None, Dict[str, Any]] = UNSET + if not isinstance(model_prop, Unset): + json_model_prop = model_prop.to_dict() if model_prop else None + params: Dict[str, Any] = { "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, } @@ -116,6 +122,8 @@ def _get_kwargs( params["union_prop_with_ref"] = json_union_prop_with_ref if enum_prop is not UNSET and enum_prop is not None: params["enum_prop"] = json_enum_prop + if model_prop is not UNSET and model_prop is not None: + params["model_prop"] = json_model_prop return { "url": url, @@ -163,6 +171,7 @@ def sync_detailed( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, @@ -179,6 +188,7 @@ def sync_detailed( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, + model_prop=model_prop, ) response = httpx.post( @@ -204,6 +214,7 @@ def sync( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ """ @@ -222,6 +233,7 @@ def sync( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, + model_prop=model_prop, ).parsed @@ -241,6 +253,7 @@ async def asyncio_detailed( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Response[Union[None, HTTPValidationError]]: kwargs = _get_kwargs( client=client, @@ -257,6 +270,7 @@ async def asyncio_detailed( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, + model_prop=model_prop, ) async with httpx.AsyncClient() as _client: @@ -281,6 +295,7 @@ async def asyncio( union_prop: Union[Unset, None, float, str] = "not a float", union_prop_with_ref: Union[Unset, None, float, AnEnum] = 0.6, enum_prop: Union[Unset, None, AnEnum] = UNSET, + model_prop: Union[Unset, None, ModelWithUnionProperty] = UNSET, ) -> Optional[Union[None, HTTPValidationError]]: """ """ @@ -300,5 +315,6 @@ async def asyncio( union_prop=union_prop, union_prop_with_ref=union_prop_with_ref, enum_prop=enum_prop, + model_prop=model_prop, ) ).parsed diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index d3ca924b3..6f5ac7423 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -1,6 +1,10 @@ """ Contains all the data models used in inputs/outputs """ from .a_model import AModel +from .a_model_model import AModelModel +from .a_model_not_required_model import AModelNotRequiredModel +from .a_model_not_required_nullable_model import AModelNotRequiredNullableModel +from .a_model_nullable_model import AModelNullableModel from .an_enum import AnEnum from .an_int_enum import AnIntEnum from .body_upload_file_tests_upload_post import BodyUploadFileTestsUploadPost diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index f811d4712..3e46992a4 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py @@ -4,6 +4,10 @@ import attr from dateutil.parser import isoparse +from ..models.a_model_model import AModelModel +from ..models.a_model_not_required_model import AModelNotRequiredModel +from ..models.a_model_not_required_nullable_model import AModelNotRequiredNullableModel +from ..models.a_model_nullable_model import AModelNullableModel from ..models.an_enum import AnEnum from ..models.different_enum import DifferentEnum from ..types import UNSET, Unset @@ -17,12 +21,16 @@ class AModel: a_camel_date_time: Union[datetime.datetime, datetime.date] a_date: datetime.date required_not_nullable: str + model: AModelModel a_nullable_date: Optional[datetime.date] required_nullable: Optional[str] + nullable_model: Optional[AModelNullableModel] nested_list_of_enums: Union[Unset, List[List[DifferentEnum]]] = UNSET attr_1_leading_digit: Union[Unset, str] = UNSET not_required_nullable: Union[Unset, None, str] = UNSET not_required_not_nullable: Union[Unset, str] = UNSET + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + not_required_nullable_model: Union[Unset, None, AModelNotRequiredNullableModel] = UNSET def to_dict(self) -> Dict[str, Any]: an_enum_value = self.an_enum_value.value @@ -35,6 +43,8 @@ def to_dict(self) -> Dict[str, Any]: a_date = self.a_date.isoformat() required_not_nullable = self.required_not_nullable + model = self.model.to_dict() + nested_list_of_enums: Union[Unset, List[Any]] = UNSET if not isinstance(self.nested_list_of_enums, Unset): nested_list_of_enums = [] @@ -52,6 +62,17 @@ def to_dict(self) -> Dict[str, Any]: required_nullable = self.required_nullable not_required_nullable = self.not_required_nullable not_required_not_nullable = self.not_required_not_nullable + nullable_model = self.nullable_model.to_dict() if self.nullable_model else None + + not_required_model: Union[Unset, Dict[str, Any]] = UNSET + if not isinstance(self.not_required_model, Unset): + not_required_model = self.not_required_model.to_dict() + + not_required_nullable_model: Union[Unset, None, Dict[str, Any]] = UNSET + if not isinstance(self.not_required_nullable_model, Unset): + not_required_nullable_model = ( + self.not_required_nullable_model.to_dict() if self.not_required_nullable_model else None + ) field_dict: Dict[str, Any] = {} field_dict.update( @@ -60,8 +81,10 @@ def to_dict(self) -> Dict[str, Any]: "aCamelDateTime": a_camel_date_time, "a_date": a_date, "required_not_nullable": required_not_nullable, + "model": model, "a_nullable_date": a_nullable_date, "required_nullable": required_nullable, + "nullable_model": nullable_model, } ) if nested_list_of_enums is not UNSET: @@ -72,6 +95,10 @@ def to_dict(self) -> Dict[str, Any]: field_dict["not_required_nullable"] = not_required_nullable if not_required_not_nullable is not UNSET: field_dict["not_required_not_nullable"] = not_required_not_nullable + if not_required_model is not UNSET: + field_dict["not_required_model"] = not_required_model + if not_required_nullable_model is not UNSET: + field_dict["not_required_nullable_model"] = not_required_nullable_model return field_dict @@ -99,6 +126,8 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat required_not_nullable = d.pop("required_not_nullable") + model = AModelModel.from_dict(d.pop("model")) + nested_list_of_enums = [] _nested_list_of_enums = d.pop("nested_list_of_enums", UNSET) for nested_list_of_enums_item_data in _nested_list_of_enums or []: @@ -124,17 +153,35 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) + nullable_model = AModelNullableModel.from_dict(d.pop("nullable_model")) + + not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET + _not_required_model = d.pop("not_required_model", UNSET) + if _not_required_model is not None and not isinstance(_not_required_model, Unset): + not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) + + not_required_nullable_model = None + _not_required_nullable_model = d.pop("not_required_nullable_model", UNSET) + if _not_required_nullable_model is not None and not isinstance(_not_required_nullable_model, Unset): + not_required_nullable_model = AModelNotRequiredNullableModel.from_dict( + cast(Dict[str, Any], _not_required_nullable_model) + ) + a_model = AModel( an_enum_value=an_enum_value, a_camel_date_time=a_camel_date_time, a_date=a_date, required_not_nullable=required_not_nullable, + model=model, nested_list_of_enums=nested_list_of_enums, a_nullable_date=a_nullable_date, attr_1_leading_digit=attr_1_leading_digit, required_nullable=required_nullable, not_required_nullable=not_required_nullable, not_required_not_nullable=not_required_not_nullable, + nullable_model=nullable_model, + not_required_model=not_required_model, + not_required_nullable_model=not_required_nullable_model, ) return a_model diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py new file mode 100644 index 000000000..0700798c8 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_model = AModelModel( + a_property=a_property, + ) + + a_model_model.additional_properties = d + return a_model_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py new file mode 100644 index 000000000..a08f547aa --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNotRequiredModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_not_required_model = AModelNotRequiredModel( + a_property=a_property, + ) + + a_model_not_required_model.additional_properties = d + return a_model_not_required_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py new file mode 100644 index 000000000..52ee2ddc3 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_not_required_nullable_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNotRequiredNullableModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNotRequiredNullableModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_not_required_nullable_model = AModelNotRequiredNullableModel( + a_property=a_property, + ) + + a_model_not_required_nullable_model.additional_properties = d + return a_model_not_required_nullable_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py new file mode 100644 index 000000000..2f21cc5ac --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_nullable_model.py @@ -0,0 +1,85 @@ +from typing import Any, Dict, List, Union + +import attr + +from ..models.an_enum import AnEnum +from ..models.an_int_enum import AnIntEnum +from ..types import UNSET, Unset + + +@attr.s(auto_attribs=True) +class AModelNullableModel: + """ """ + + a_property: Union[Unset, AnEnum, AnIntEnum] = UNSET + additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict) + + def to_dict(self) -> Dict[str, Any]: + a_property: Union[Unset, int] + if isinstance(self.a_property, Unset): + a_property = UNSET + elif isinstance(self.a_property, AnEnum): + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + else: + a_property = UNSET + if not isinstance(self.a_property, Unset): + a_property = self.a_property.value + + field_dict: Dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if a_property is not UNSET: + field_dict["a_property"] = a_property + + return field_dict + + @staticmethod + def from_dict(src_dict: Dict[str, Any]) -> "AModelNullableModel": + d = src_dict.copy() + + def _parse_a_property(data: Any) -> Union[Unset, AnEnum, AnIntEnum]: + data = None if isinstance(data, Unset) else data + a_property: Union[Unset, AnEnum, AnIntEnum] + try: + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnEnum(_a_property) + + return a_property + except: # noqa: E722 + pass + a_property = UNSET + _a_property = data + if _a_property is not None: + a_property = AnIntEnum(_a_property) + + return a_property + + a_property = _parse_a_property(d.pop("a_property", UNSET)) + + a_model_nullable_model = AModelNullableModel( + a_property=a_property, + ) + + a_model_nullable_model.additional_properties = d + return a_model_nullable_model + + @property + def additional_keys(self) -> List[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 931fc481e..6d7d15c6c 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -433,6 +433,14 @@ }, "name": "enum_prop", "in": "query" + }, + { + "required": false, + "schema": { + "$ref": "#/components/schemas/ModelWithUnionProperty" + }, + "name": "model_prop", + "in": "query" } ], "responses": { @@ -659,7 +667,7 @@ "schemas": { "AModel": { "title": "AModel", - "required": ["an_enum_value", "aCamelDateTime", "a_date", "a_nullable_date", "required_nullable", "required_not_nullable"], + "required": ["an_enum_value", "aCamelDateTime", "a_date", "a_nullable_date", "required_nullable", "required_not_nullable", "model", "nullable_model"], "type": "object", "properties": { "an_enum_value": { @@ -723,6 +731,42 @@ "title": "NOT Required AND NOT Nullable", "type": "string", "nullable": false + }, + "model": { + "type": "object", + "allOf": [ + { + "ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": false + }, + "nullable_model": { + "type": "object", + "allOf": [ + { + "ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": true + }, + "not_required_model": { + "type": "object", + "allOf": [ + { + "ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": false + }, + "not_required_nullable_model": { + "type": "object", + "allOf": [ + { + "ref": "#/components/schemas/ModelWithUnionProperty" + } + ], + "nullable": true } }, "description": "A Model for testing all the ways custom objects can be used ", diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index e3a3f69b7..a5ebf00d4 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -272,13 +272,23 @@ def build_model_property( required_properties: List[Property] = [] optional_properties: List[Property] = [] relative_imports: Set[str] = set() + references: List[oai.Reference] = [] class_name = data.title or name if parent_name: class_name = f"{utils.pascal_case(parent_name)}{utils.pascal_case(class_name)}" ref = Reference.from_ref(class_name) - for key, value in (data.properties or {}).items(): + all_props = data.properties or {} + if not isinstance(data, oai.Reference) and data.allOf: + for sub_prop in data.allOf: + if isinstance(sub_prop, oai.Reference): + references += [sub_prop] + else: + all_props.update(sub_prop.properties or {}) + required_set.update(sub_prop.required or []) + + for key, value in all_props.items(): prop_required = key in required_set prop, schemas = property_from_data( name=key, required=prop_required, data=value, schemas=schemas, parent_name=class_name @@ -314,6 +324,7 @@ def build_model_property( prop = ModelProperty( reference=ref, + references=references, required_properties=required_properties, optional_properties=optional_properties, relative_imports=relative_imports, @@ -471,9 +482,7 @@ def _property_from_data( ) if data.anyOf or data.oneOf: return build_union_property(data=data, name=name, required=required, schemas=schemas, parent_name=parent_name) - if not data.type: - return NoneProperty(name=name, required=required, nullable=False, default=None), schemas - + if data.type == "string": return _string_based_property(name=name, required=required, data=data), schemas elif data.type == "number": @@ -508,8 +517,10 @@ def _property_from_data( ) elif data.type == "array": return build_list_property(data=data, name=name, required=required, schemas=schemas, parent_name=parent_name) - elif data.type == "object": + elif data.type == "object" or data.allOf: return build_model_property(data=data, name=name, schemas=schemas, required=required, parent_name=parent_name) + elif not data.type: + return NoneProperty(name=name, required=required, nullable=False, default=None), schemas return PropertyError(data=data, detail=f"unknown type {data.type}"), schemas @@ -566,6 +577,15 @@ def build_schemas(*, components: Dict[str, Union[oai.Reference, oai.Schema]]) -> schemas = schemas_or_err processing = True # We made some progress this round, do another after it's done to_process = next_round - schemas.errors.extend(errors) + resolve_errors: List[PropertyError] = [] + models = list(schemas.models.values()) + for model in models: + schemas_or_err = model.resolve_references(components=components, schemas=schemas) + if isinstance(schemas_or_err, PropertyError): + resolve_errors.append(schemas_or_err) + else: + schemas = schemas_or_err + + schemas.errors.extend(resolve_errors) return schemas diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 3ab145af4..421259b9d 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -1,16 +1,25 @@ -from typing import ClassVar, List, Set, Union +from __future__ import annotations + +from collections.abc import Iterable +from typing import TYPE_CHECKING, ClassVar, Dict, List, Set, Union import attr +from ... import schema as oai +from ..errors import PropertyError from ..reference import Reference from .property import Property +if TYPE_CHECKING: + from .schemas import Schemas + @attr.s(auto_attribs=True, frozen=True) class ModelProperty(Property): """ A property which refers to another Schema """ reference: Reference + references: List[oai.Reference] required_properties: List[Property] optional_properties: List[Property] @@ -20,6 +29,50 @@ class ModelProperty(Property): _json_type_string: ClassVar[str] = "Dict[str, Any]" template: ClassVar[str] = "model_property.pyi" + json_is_dict: ClassVar[bool] = True + + def resolve_references( + self, components: Dict[str, Union[oai.Reference, oai.Schema]], schemas: Schemas + ) -> Union[Schemas, PropertyError]: + from ..properties import property_from_data + + required_set = set() + props = {} + while self.references: + reference = self.references.pop() + source_name = Reference.from_ref(reference.ref).class_name + referenced_prop = components[source_name] + 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 []: + if isinstance(sub_prop, oai.Reference): + self.references.append(sub_prop) + else: + for p, val in (sub_prop.properties or {}).items(): + props[p] = (val, source_name) + if isinstance(referenced_prop.required, Iterable): + for sub_prop_name in referenced_prop.required: + required_set.add(sub_prop_name) + + for key, (value, source_name) in (props or {}).items(): + required = key in required_set + prop, schemas = property_from_data( + name=key, required=required, data=value, schemas=schemas, parent_name=source_name + ) + if isinstance(prop, PropertyError): + return prop + if required: + self.required_properties.append(prop) + # Remove the optional version + new_optional_props = [op for op in self.optional_properties if op.name != prop.name] + self.optional_properties.clear() + self.optional_properties.extend(new_optional_props) + elif not any(ep for ep in (self.optional_properties + self.required_properties) if ep.name == prop.name): + self.optional_properties.append(prop) + self.relative_imports.update(prop.get_imports(prefix="..")) + + return schemas def get_base_type_string(self) -> str: return self.reference.class_name diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index 651370ae2..c0ec0d56e 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -29,6 +29,7 @@ class Property: python_name: str = attr.ib(init=False) template: ClassVar[Optional[str]] = None + json_is_dict: ClassVar[bool] = False def __attrs_post_init__(self) -> None: object.__setattr__(self, "python_name", utils.to_valid_python_identifier(utils.snake_case(self.name))) diff --git a/pyproject.toml b/pyproject.toml index 4be8d93db..4d74fea82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,6 @@ safety = "*" pytest-cov = "*" python-multipart = "*" flake8 = "*" -typer-cli = "^0.0.11" [tool.taskipy.tasks] check = """ @@ -67,7 +66,12 @@ task regen\ && task regen_custom\ && task e2e\ """ -docs = "typer openapi_python_client/cli.py utils docs > usage.md" +gen-setuppy = """ +poetry build \ +&& tar --strip-components=1 -xvf "$(ls -1 dist/*tar.gz | tail -1)" '*/setup.py' \ +&& isort setup.py \ +&& black setup.py +""" [tool.black] line-length = 120 @@ -95,5 +99,8 @@ skip = [".venv", "tests/test_templates"] omit = ["openapi_python_client/templates/*"] [build-system] -requires = ["poetry>=1.0"] +requires = [ + "setuptools>=30.3.0,<50", + "poetry>=1.0" +] build-backend = "poetry.masonry.api" From 97063d576e6f1cf4a2772c3e11659122def9d2a2 Mon Sep 17 00:00:00 2001 From: Forest Tong Date: Wed, 27 Jan 2021 18:33:57 -0500 Subject: [PATCH 4/5] Fix tests --- .../parser/properties/__init__.py | 2 +- .../parser/properties/model_property.py | 1 - .../templates/endpoint_macros.pyi | 9 +- .../property_templates/model_property.pyi | 4 +- .../test_parser/test_properties/test_init.py | 22 ++- .../test_properties/test_model_property.py | 142 ++++++++++++++++++ 6 files changed, 167 insertions(+), 13 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index a5ebf00d4..b84166f23 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -482,7 +482,6 @@ def _property_from_data( ) if data.anyOf or data.oneOf: return build_union_property(data=data, name=name, required=required, schemas=schemas, parent_name=parent_name) - if data.type == "string": return _string_based_property(name=name, required=required, data=data), schemas elif data.type == "number": @@ -587,5 +586,6 @@ def build_schemas(*, components: Dict[str, Union[oai.Reference, oai.Schema]]) -> else: schemas = schemas_or_err + schemas.errors.extend(errors) schemas.errors.extend(resolve_errors) return schemas diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 421259b9d..c4c203a5d 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -20,7 +20,6 @@ class ModelProperty(Property): reference: Reference references: List[oai.Reference] - required_properties: List[Property] optional_properties: List[Property] description: str diff --git a/openapi_python_client/templates/endpoint_macros.pyi b/openapi_python_client/templates/endpoint_macros.pyi index 7a1862be3..6586ecd70 100644 --- a/openapi_python_client/templates/endpoint_macros.pyi +++ b/openapi_python_client/templates/endpoint_macros.pyi @@ -33,11 +33,12 @@ params: Dict[str, Any] = { } {% for property in endpoint.query_parameters %} {% if not property.required or property.nullable %} -if {% if not property.required %}{{ property.python_name }} is not UNSET and {% endif %}{{ property.python_name }} is not None: - {% if property.template %} - params["{{ property.name }}"] = {{ "json_" + property.python_name }} + {% set property_name = "json_" + property.python_name if property.template else property.python_name %} +if {% if not property.required %}not isinstance({{ property_name }}, Unset) and {% endif %}{{ property_name }} is not None: + {% if property.json_is_dict %} + params.update({{ property_name }}) {% else %} - params["{{ property.name }}"] = {{ property.python_name }} + params["{{ property.name }}"] = {{ property_name }} {% endif %} {% endif %} {% endfor %} diff --git a/openapi_python_client/templates/property_templates/model_property.pyi b/openapi_python_client/templates/property_templates/model_property.pyi index dfda61d97..6aef71d0f 100644 --- a/openapi_python_client/templates/property_templates/model_property.pyi +++ b/openapi_python_client/templates/property_templates/model_property.pyi @@ -1,5 +1,5 @@ {% macro construct(property, source, initial_value=None) %} -{% if property.required %} +{% if property.required and not property.nullable %} {{ property.python_name }} = {{ property.reference.class_name }}.from_dict({{ source }}) {% else %} {% if initial_value != None %} @@ -10,7 +10,7 @@ {{ property.python_name }}: {{ property.get_type_string() }} = UNSET {% endif %} _{{ property.python_name }} = {{source}} -if _{{ property.python_name }} is not None and not isinstance(_{{ property.python_name }}, Unset): +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 %}: {{ property.python_name }} = {{ property.reference.class_name }}.from_dict(cast(Dict[str, Any], _{{ property.python_name }})) {% endif %} {% endmacro %} diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 2f1c71cc5..604b1bd1d 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -584,6 +584,7 @@ def test_property_from_data_ref_model(self): nullable=False, default=None, reference=Reference(class_name=class_name, module_name="my_model"), + references=[], required_properties=[], optional_properties=[], description="", @@ -600,6 +601,7 @@ def test_property_from_data_ref_model(self): nullable=False, default=None, reference=Reference(class_name=class_name, module_name="my_model"), + references=[], required_properties=[], optional_properties=[], description="", @@ -990,15 +992,20 @@ def test_build_schemas(mocker): schemas_1 = mocker.MagicMock() model_2 = mocker.MagicMock() schemas_2 = mocker.MagicMock(errors=[]) - error = PropertyError() + schemas_2.models = {"1": model_1, "2": model_2} + error_1 = PropertyError() schemas_3 = mocker.MagicMock() + schemas_4 = mocker.MagicMock(errors=[]) + model_1.resolve_references.return_value = schemas_4 + error_2 = PropertyError() + model_2.resolve_references.return_value = error_2 # This loops through one for each, then again to retry the error build_model_property.side_effect = [ (model_1, schemas_1), (model_2, schemas_2), - (error, schemas_3), - (error, schemas_3), + (error_1, schemas_3), + (error_1, schemas_3), ] from openapi_python_client.parser.properties import Schemas, build_schemas @@ -1014,8 +1021,12 @@ def test_build_schemas(mocker): ] ) # schemas_3 was the last to come back from build_model_property, but it should be ignored because it's an error - assert result == schemas_2 - assert result.errors == [error] + model_1.resolve_references.assert_called_once_with(components=in_data, schemas=schemas_2) + # schemas_4 came from resolving model_1 + model_2.resolve_references.assert_called_once_with(components=in_data, schemas=schemas_4) + # resolving model_2 resulted in err, so no schemas_5 + assert result == schemas_4 + assert result.errors == [error_1, error_2] def test_build_parse_error_on_reference(): @@ -1089,6 +1100,7 @@ def test_build_model_property(additional_properties_schema, expected_additional_ nullable=False, default=None, reference=Reference(class_name="ParentMyModel", module_name="parent_my_model"), + references=[], required_properties=[StringProperty(name="req", required=True, nullable=False, default=None)], optional_properties=[DateTimeProperty(name="opt", required=False, nullable=False, default=None)], description=data.description, diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index 410112666..83d521ab1 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -23,6 +23,7 @@ def test_get_type_string(no_optional, nullable, required, expected): nullable=nullable, default=None, reference=Reference(class_name="MyClass", module_name="my_module"), + references=[], description="", optional_properties=[], required_properties=[], @@ -42,6 +43,7 @@ def test_get_imports(): nullable=True, default=None, reference=Reference(class_name="MyClass", module_name="my_module"), + references=[], description="", optional_properties=[], required_properties=[], @@ -57,3 +59,143 @@ def test_get_imports(): "from typing import Dict", "from typing import cast", } + + +def test_resolve_references(mocker): + import openapi_python_client.schema as oai + from openapi_python_client.parser.properties import build_model_property + + schemas = { + "RefA": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["String"], + properties={ + "String": oai.Schema.construct(type="string"), + "Enum": oai.Schema.construct(type="string", enum=["aValue"]), + "DateTime": oai.Schema.construct(type="string", format="date-time"), + }, + ), + "RefB": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["DateTime"], + properties={ + "Int": oai.Schema.construct(type="integer"), + "DateTime": oai.Schema.construct(type="string", format="date-time"), + "Float": oai.Schema.construct(type="number", format="float"), + }, + ), + # Intentionally no properties defined + "RefC": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + ), + } + + model_schema = oai.Schema.construct( + allOf=[ + oai.Reference.construct(ref="#/components/schemas/RefA"), + oai.Reference.construct(ref="#/components/schemas/RefB"), + oai.Reference.construct(ref="#/components/schemas/RefC"), + oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["Float"], + properties={ + "String": oai.Schema.construct(type="string"), + "Float": oai.Schema.construct(type="number", format="float"), + }, + ), + ] + ) + + components = {**schemas, "Model": model_schema} + + from openapi_python_client.parser.properties import Schemas + + schemas_holder = Schemas() + model, schemas_holder = build_model_property( + data=model_schema, name="Model", required=True, schemas=schemas_holder, parent_name=None + ) + model.resolve_references(components, schemas_holder) + assert sorted(p.name for p in model.required_properties) == ["DateTime", "Float", "String"] + assert all(p.required for p in model.required_properties) + assert sorted(p.name for p in model.optional_properties) == ["Enum", "Int"] + assert all(not p.required for p in model.optional_properties) + + +def test_resolve_references_nested_allof(mocker): + import openapi_python_client.schema as oai + from openapi_python_client.parser.properties import build_model_property + + schemas = { + "RefA": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["String"], + properties={ + "String": oai.Schema.construct(type="string"), + "Enum": oai.Schema.construct(type="string", enum=["aValue"]), + "DateTime": oai.Schema.construct(type="string", format="date-time"), + }, + ), + "RefB": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["DateTime"], + properties={ + "Int": oai.Schema.construct(type="integer"), + "DateTime": oai.Schema.construct(type="string", format="date-time"), + "Float": oai.Schema.construct(type="number", format="float"), + }, + ), + # Intentionally no properties defined + "RefC": oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + ), + } + + model_schema = oai.Schema.construct( + type="object", + properties={ + "Key": oai.Schema.construct( + allOf=[ + oai.Reference.construct(ref="#/components/schemas/RefA"), + oai.Reference.construct(ref="#/components/schemas/RefB"), + oai.Reference.construct(ref="#/components/schemas/RefC"), + oai.Schema.construct( + title=mocker.MagicMock(), + description=mocker.MagicMock(), + required=["Float"], + properties={ + "String": oai.Schema.construct(type="string"), + "Float": oai.Schema.construct(type="number", format="float"), + }, + ), + ] + ), + }, + ) + + components = {**schemas, "Model": model_schema} + + from openapi_python_client.parser.properties import ModelProperty, Schemas + + schemas_holder = Schemas() + model, schemas_holder = build_model_property( + data=model_schema, name="Model", required=True, schemas=schemas_holder, parent_name=None + ) + model.resolve_references(components, schemas_holder) + assert sorted(p.name for p in model.required_properties) == [] + assert sorted(p.name for p in model.optional_properties) == ["Key"] + assert all(not p.required for p in model.optional_properties) + + key_property = model.optional_properties[0] + assert isinstance(key_property, ModelProperty) + key_property.resolve_references(components, schemas_holder) + assert sorted(p.name for p in key_property.required_properties) == ["DateTime", "Float", "String"] + assert all(p.required for p in key_property.required_properties) + assert sorted(p.name for p in key_property.optional_properties) == ["Enum", "Int"] + assert all(not p.required for p in key_property.optional_properties) From 67bce4180461c10a76f2355a7efaaae167b62d8d Mon Sep 17 00:00:00 2001 From: Forest Tong Date: Wed, 27 Jan 2021 18:34:26 -0500 Subject: [PATCH 5/5] Regen --- .../api/tests/defaults_tests_defaults_post.py | 34 +++++++++++-------- ...tional_value_tests_optional_query_param.py | 2 +- .../custom_e2e/models/a_model.py | 7 ++-- ...el_with_primitive_additional_properties.py | 2 +- .../api/tests/defaults_tests_defaults_post.py | 34 +++++++++++-------- ...tional_value_tests_optional_query_param.py | 2 +- .../my_test_api_client/models/a_model.py | 7 ++-- ...el_with_primitive_additional_properties.py | 2 +- 8 files changed, 54 insertions(+), 36 deletions(-) diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py index e4b71e321..d61560254 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/defaults_tests_defaults_post.py @@ -122,32 +122,38 @@ def httpx_request( params: Dict[str, Any] = { "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, } - if string_prop is not UNSET and string_prop is not None: + if not isinstance(string_prop, Unset) and string_prop is not None: params["string_prop"] = string_prop - if not_required_not_nullable_datetime_prop is not UNSET and not_required_not_nullable_datetime_prop is not None: + if ( + not isinstance(json_not_required_not_nullable_datetime_prop, Unset) + and json_not_required_not_nullable_datetime_prop is not None + ): params["not_required_not_nullable_datetime_prop"] = json_not_required_not_nullable_datetime_prop - if not_required_nullable_datetime_prop is not UNSET and not_required_nullable_datetime_prop is not None: + if ( + not isinstance(json_not_required_nullable_datetime_prop, Unset) + and json_not_required_nullable_datetime_prop is not None + ): params["not_required_nullable_datetime_prop"] = json_not_required_nullable_datetime_prop - if required_nullable_datetime_prop is not None: + if json_required_nullable_datetime_prop is not None: params["required_nullable_datetime_prop"] = json_required_nullable_datetime_prop - if date_prop is not UNSET and date_prop is not None: + if not isinstance(json_date_prop, Unset) and json_date_prop is not None: params["date_prop"] = json_date_prop - if float_prop is not UNSET and float_prop is not None: + if not isinstance(float_prop, Unset) and float_prop is not None: params["float_prop"] = float_prop - if int_prop is not UNSET and int_prop is not None: + if not isinstance(int_prop, Unset) and int_prop is not None: params["int_prop"] = int_prop - if boolean_prop is not UNSET and boolean_prop is not None: + if not isinstance(boolean_prop, Unset) and boolean_prop is not None: params["boolean_prop"] = boolean_prop - if list_prop is not UNSET and list_prop is not None: + if not isinstance(json_list_prop, Unset) and json_list_prop is not None: params["list_prop"] = json_list_prop - if union_prop is not UNSET and union_prop is not None: + if not isinstance(json_union_prop, Unset) and json_union_prop is not None: params["union_prop"] = json_union_prop - if union_prop_with_ref is not UNSET and union_prop_with_ref is not None: + if not isinstance(json_union_prop_with_ref, Unset) and json_union_prop_with_ref is not None: params["union_prop_with_ref"] = json_union_prop_with_ref - if enum_prop is not UNSET and enum_prop is not None: + if not isinstance(json_enum_prop, Unset) and json_enum_prop is not None: params["enum_prop"] = json_enum_prop - if model_prop is not UNSET and model_prop is not None: - params["model_prop"] = json_model_prop + if not isinstance(json_model_prop, Unset) and json_model_prop is not None: + params.update(json_model_prop) response = client.request( "post", diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py index 8ff5a52a3..279ce8ca8 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/api/tests/optional_value_tests_optional_query_param.py @@ -47,7 +47,7 @@ def httpx_request( json_query_param = query_param params: Dict[str, Any] = {} - if query_param is not UNSET and query_param is not None: + if not isinstance(json_query_param, Unset) and json_query_param is not None: params["query_param"] = json_query_param response = client.request( diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py index 3e46992a4..9462c38b9 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/a_model.py @@ -153,11 +153,14 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - nullable_model = AModelNullableModel.from_dict(d.pop("nullable_model")) + nullable_model = None + _nullable_model = d.pop("nullable_model") + if _nullable_model is not None: + nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model)) not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET _not_required_model = d.pop("not_required_model", UNSET) - if _not_required_model is not None and not isinstance(_not_required_model, Unset): + if not isinstance(_not_required_model, Unset): not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) not_required_nullable_model = None diff --git a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py index feb30878d..1ba447347 100644 --- a/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py +++ b/end_to_end_tests/golden-record-custom/custom_e2e/models/model_with_primitive_additional_properties.py @@ -33,7 +33,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperti d = src_dict.copy() a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) - if _a_date_holder is not None and not isinstance(_a_date_holder, Unset): + if not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( cast(Dict[str, Any], _a_date_holder) ) diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py index 540f68b44..f87317ead 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py @@ -98,32 +98,38 @@ def _get_kwargs( params: Dict[str, Any] = { "required_not_nullable_datetime_prop": json_required_not_nullable_datetime_prop, } - if string_prop is not UNSET and string_prop is not None: + if not isinstance(string_prop, Unset) and string_prop is not None: params["string_prop"] = string_prop - if not_required_not_nullable_datetime_prop is not UNSET and not_required_not_nullable_datetime_prop is not None: + if ( + not isinstance(json_not_required_not_nullable_datetime_prop, Unset) + and json_not_required_not_nullable_datetime_prop is not None + ): params["not_required_not_nullable_datetime_prop"] = json_not_required_not_nullable_datetime_prop - if not_required_nullable_datetime_prop is not UNSET and not_required_nullable_datetime_prop is not None: + if ( + not isinstance(json_not_required_nullable_datetime_prop, Unset) + and json_not_required_nullable_datetime_prop is not None + ): params["not_required_nullable_datetime_prop"] = json_not_required_nullable_datetime_prop - if required_nullable_datetime_prop is not None: + if json_required_nullable_datetime_prop is not None: params["required_nullable_datetime_prop"] = json_required_nullable_datetime_prop - if date_prop is not UNSET and date_prop is not None: + if not isinstance(json_date_prop, Unset) and json_date_prop is not None: params["date_prop"] = json_date_prop - if float_prop is not UNSET and float_prop is not None: + if not isinstance(float_prop, Unset) and float_prop is not None: params["float_prop"] = float_prop - if int_prop is not UNSET and int_prop is not None: + if not isinstance(int_prop, Unset) and int_prop is not None: params["int_prop"] = int_prop - if boolean_prop is not UNSET and boolean_prop is not None: + if not isinstance(boolean_prop, Unset) and boolean_prop is not None: params["boolean_prop"] = boolean_prop - if list_prop is not UNSET and list_prop is not None: + if not isinstance(json_list_prop, Unset) and json_list_prop is not None: params["list_prop"] = json_list_prop - if union_prop is not UNSET and union_prop is not None: + if not isinstance(json_union_prop, Unset) and json_union_prop is not None: params["union_prop"] = json_union_prop - if union_prop_with_ref is not UNSET and union_prop_with_ref is not None: + if not isinstance(json_union_prop_with_ref, Unset) and json_union_prop_with_ref is not None: params["union_prop_with_ref"] = json_union_prop_with_ref - if enum_prop is not UNSET and enum_prop is not None: + if not isinstance(json_enum_prop, Unset) and json_enum_prop is not None: params["enum_prop"] = json_enum_prop - if model_prop is not UNSET and model_prop is not None: - params["model_prop"] = json_model_prop + if not isinstance(json_model_prop, Unset) and json_model_prop is not None: + params.update(json_model_prop) return { "url": url, diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py index 2c14c2949..23bd9e741 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/optional_value_tests_optional_query_param.py @@ -24,7 +24,7 @@ def _get_kwargs( json_query_param = query_param params: Dict[str, Any] = {} - if query_param is not UNSET and query_param is not None: + if not isinstance(json_query_param, Unset) and json_query_param is not None: params["query_param"] = json_query_param return { diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py index 3e46992a4..9462c38b9 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py @@ -153,11 +153,14 @@ def _parse_a_camel_date_time(data: Any) -> Union[datetime.datetime, datetime.dat not_required_not_nullable = d.pop("not_required_not_nullable", UNSET) - nullable_model = AModelNullableModel.from_dict(d.pop("nullable_model")) + nullable_model = None + _nullable_model = d.pop("nullable_model") + if _nullable_model is not None: + nullable_model = AModelNullableModel.from_dict(cast(Dict[str, Any], _nullable_model)) not_required_model: Union[Unset, AModelNotRequiredModel] = UNSET _not_required_model = d.pop("not_required_model", UNSET) - if _not_required_model is not None and not isinstance(_not_required_model, Unset): + if not isinstance(_not_required_model, Unset): not_required_model = AModelNotRequiredModel.from_dict(cast(Dict[str, Any], _not_required_model)) not_required_nullable_model = None diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py index feb30878d..1ba447347 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_primitive_additional_properties.py @@ -33,7 +33,7 @@ def from_dict(src_dict: Dict[str, Any]) -> "ModelWithPrimitiveAdditionalProperti d = src_dict.copy() a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET _a_date_holder = d.pop("a_date_holder", UNSET) - if _a_date_holder is not None and not isinstance(_a_date_holder, Unset): + if not isinstance(_a_date_holder, Unset): a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict( cast(Dict[str, Any], _a_date_holder) )