diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2859fe41a..82fdf8bef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 0.9.1
+
+### Features
+
+- Allow references to non-object, non-enum types [#371][#418][#425]. Thanks @p1-ra!
+- Allow for attrs 21.x in generated clients [#412]
+- Allow for using any version of Black [#416] [#411]. Thanks @christhekeele!
+
+### Fixes
+
+- Prevent crash when providing a non-string default to a string attribute. [#414] [#415]
+- Deserialization of optional nullable properties when no value is returned from the API [#420] [#381]. Thanks @forest-benchling!
+
 ## 0.9.0 - 2021-05-04
 
 ### Breaking Changes
@@ -45,9 +58,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
   - `none` will not create a project folder at all, only the inner package folder (which won't be inner anymore)
 
 - Attempt to detect and alert users if they are using an unsupported version of OpenAPI (#281).
+
 - The media type application/vnd.api+json will now be handled just like application/json (#307). Thanks @jrversteegh!
+
 - Support passing models into query parameters (#316). Thanks @forest-benchling!
+
 - Add support for cookie parameters (#326).
+
 - New `--file-encoding` command line option (#330). Sets the encoding used when writing generated files (defaults to utf-8). Thanks @dongfangtianyu!
 
 ### Changes
@@ -96,13 +113,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 ### Breaking Changes
 
 - Any request/response field that is not `required` and wasn't specified is now set to `UNSET` instead of `None`.
+
 - Values that are `UNSET` will not be sent along in API calls
+
 - Schemas defined with `type=object` will now be converted into classes, just like if they were created as ref components. The previous behavior was a combination of skipping and using generic Dicts for these schemas.
+
 - Response schema handling was unified with input schema handling, meaning that responses will behave differently than before. Specifically, instead of the content-type deciding what the generated Python type is, the schema itself will.
 
   - As a result of this, endpoints that used to return `bytes` when content-type was application/octet-stream will now return a `File` object if the type of the data is "binary", just like if you were submitting that type instead of receiving it.
 
 - Instead of skipping input properties with no type, enum, anyOf, or oneOf declared, the property will be declared as `None`.
+
 - Class (models and Enums) names will now contain the name of their parent element (if any). For example, a property declared in an endpoint will be named like {endpoint*name}*{previous*class*name}. Classes will no longer be deduplicated by appending a number to the end of the generated name, so if two names conflict with this new naming scheme, there will be an error instead.
 
 ### Additions
@@ -268,6 +289,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 ### Changes
 
 - The way most imports are handled was changed which _should_ lead to fewer unused imports in generated files.
+
 - Better error messages
 
   - Most error messages will contain some useful information about why it failed instead of a stack trace
diff --git a/dobby.toml b/dobby.toml
index 2a0cc02fd..855ba9f73 100644
--- a/dobby.toml
+++ b/dobby.toml
@@ -1,5 +1,5 @@
 [[workflows]]
-name = "Start Task"
+name = "task"
     [[workflows.steps]]
     type = "SelectGitHubIssue"
 
@@ -7,13 +7,13 @@ name = "Start Task"
     type = "SwitchBranches"
 
 [[workflows]]
-name = "Prepare Release"
+name = "release"
     [[workflows.steps]]
     type = "UpdateProjectFromCommits"
 
     [[workflows.steps]]
     type = "Command"
-    command = "prettier --write CHANGELOG.md"
+    command = "npx prettier --write CHANGELOG.md"
 
 [github]
 owner = "triaxtec"
diff --git a/end_to_end_tests/custom-templates-golden-record/README.md b/end_to_end_tests/custom-templates-golden-record/README.md
new file mode 100644
index 000000000..e5106eea7
--- /dev/null
+++ b/end_to_end_tests/custom-templates-golden-record/README.md
@@ -0,0 +1 @@
+my-test-api-client
\ No newline at end of file
diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py
new file mode 100644
index 000000000..3ee5dbaf0
--- /dev/null
+++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py
@@ -0,0 +1,21 @@
+""" Contains methods for accessing the API """
+
+from typing import Type
+
+from my_test_api_client.api.default import DefaultEndpoints
+from my_test_api_client.api.parameters import ParametersEndpoints
+from my_test_api_client.api.tests import TestsEndpoints
+
+
+class MyTestApiClientApi:
+    @classmethod
+    def tests(cls) -> Type[TestsEndpoints]:
+        return TestsEndpoints
+
+    @classmethod
+    def default(cls) -> Type[DefaultEndpoints]:
+        return DefaultEndpoints
+
+    @classmethod
+    def parameters(cls) -> Type[ParametersEndpoints]:
+        return ParametersEndpoints
diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py
new file mode 100644
index 000000000..4d0eb4fb5
--- /dev/null
+++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/default/__init__.py
@@ -0,0 +1,15 @@
+""" Contains methods for accessing the API Endpoints """
+
+import types
+
+from my_test_api_client.api.default import get_common_parameters, post_common_parameters
+
+
+class DefaultEndpoints:
+    @classmethod
+    def get_common_parameters(cls) -> types.ModuleType:
+        return get_common_parameters
+
+    @classmethod
+    def post_common_parameters(cls) -> types.ModuleType:
+        return post_common_parameters
diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/parameters/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/parameters/__init__.py
new file mode 100644
index 000000000..b92c6d96b
--- /dev/null
+++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/parameters/__init__.py
@@ -0,0 +1,11 @@
+""" Contains methods for accessing the API Endpoints """
+
+import types
+
+from my_test_api_client.api.parameters import get_same_name_multiple_locations_param
+
+
+class ParametersEndpoints:
+    @classmethod
+    def get_same_name_multiple_locations_param(cls) -> types.ModuleType:
+        return get_same_name_multiple_locations_param
diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py
new file mode 100644
index 000000000..2cfd13809
--- /dev/null
+++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tests/__init__.py
@@ -0,0 +1,128 @@
+""" Contains methods for accessing the API Endpoints """
+
+import types
+
+from my_test_api_client.api.tests import (
+    defaults_tests_defaults_post,
+    get_basic_list_of_booleans,
+    get_basic_list_of_floats,
+    get_basic_list_of_integers,
+    get_basic_list_of_strings,
+    get_user_list,
+    int_enum_tests_int_enum_post,
+    json_body_tests_json_body_post,
+    no_response_tests_no_response_get,
+    octet_stream_tests_octet_stream_get,
+    optional_value_tests_optional_query_param,
+    test_inline_objects,
+    token_with_cookie_auth_token_with_cookie_get,
+    unsupported_content_tests_unsupported_content_get,
+    upload_file_tests_upload_post,
+)
+
+
+class TestsEndpoints:
+    @classmethod
+    def get_user_list(cls) -> types.ModuleType:
+        """
+        Get a list of things
+        """
+        return get_user_list
+
+    @classmethod
+    def get_basic_list_of_strings(cls) -> types.ModuleType:
+        """
+        Get a list of strings
+        """
+        return get_basic_list_of_strings
+
+    @classmethod
+    def get_basic_list_of_integers(cls) -> types.ModuleType:
+        """
+        Get a list of integers
+        """
+        return get_basic_list_of_integers
+
+    @classmethod
+    def get_basic_list_of_floats(cls) -> types.ModuleType:
+        """
+        Get a list of floats
+        """
+        return get_basic_list_of_floats
+
+    @classmethod
+    def get_basic_list_of_booleans(cls) -> types.ModuleType:
+        """
+        Get a list of booleans
+        """
+        return get_basic_list_of_booleans
+
+    @classmethod
+    def upload_file_tests_upload_post(cls) -> types.ModuleType:
+        """
+        Upload a file
+        """
+        return upload_file_tests_upload_post
+
+    @classmethod
+    def json_body_tests_json_body_post(cls) -> types.ModuleType:
+        """
+        Try sending a JSON body
+        """
+        return json_body_tests_json_body_post
+
+    @classmethod
+    def defaults_tests_defaults_post(cls) -> types.ModuleType:
+        """
+        Defaults
+        """
+        return defaults_tests_defaults_post
+
+    @classmethod
+    def octet_stream_tests_octet_stream_get(cls) -> types.ModuleType:
+        """
+        Octet Stream
+        """
+        return octet_stream_tests_octet_stream_get
+
+    @classmethod
+    def no_response_tests_no_response_get(cls) -> types.ModuleType:
+        """
+        No Response
+        """
+        return no_response_tests_no_response_get
+
+    @classmethod
+    def unsupported_content_tests_unsupported_content_get(cls) -> types.ModuleType:
+        """
+        Unsupported Content
+        """
+        return unsupported_content_tests_unsupported_content_get
+
+    @classmethod
+    def int_enum_tests_int_enum_post(cls) -> types.ModuleType:
+        """
+        Int Enum
+        """
+        return int_enum_tests_int_enum_post
+
+    @classmethod
+    def test_inline_objects(cls) -> types.ModuleType:
+        """
+        Test Inline Objects
+        """
+        return test_inline_objects
+
+    @classmethod
+    def optional_value_tests_optional_query_param(cls) -> types.ModuleType:
+        """
+        Test optional query parameters
+        """
+        return optional_value_tests_optional_query_param
+
+    @classmethod
+    def token_with_cookie_auth_token_with_cookie_get(cls) -> types.ModuleType:
+        """
+        Test optional cookie parameters
+        """
+        return token_with_cookie_auth_token_with_cookie_get
diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py
new file mode 100644
index 000000000..8152214c1
--- /dev/null
+++ b/end_to_end_tests/golden-record/my_test_api_client/api/tests/post_form_data.py
@@ -0,0 +1,68 @@
+from typing import Any, Dict
+
+import httpx
+
+from ...client import Client
+from ...models.a_form_data import AFormData
+from ...types import Response
+
+
+def _get_kwargs(
+    *,
+    client: Client,
+    form_data: AFormData,
+) -> Dict[str, Any]:
+    url = "{}/tests/post_form_data".format(client.base_url)
+
+    headers: Dict[str, Any] = client.get_headers()
+    cookies: Dict[str, Any] = client.get_cookies()
+
+    return {
+        "url": url,
+        "headers": headers,
+        "cookies": cookies,
+        "timeout": client.get_timeout(),
+        "data": form_data.to_dict(),
+    }
+
+
+def _build_response(*, response: httpx.Response) -> Response[None]:
+    return Response(
+        status_code=response.status_code,
+        content=response.content,
+        headers=response.headers,
+        parsed=None,
+    )
+
+
+def sync_detailed(
+    *,
+    client: Client,
+    form_data: AFormData,
+) -> Response[None]:
+    kwargs = _get_kwargs(
+        client=client,
+        form_data=form_data,
+    )
+
+    response = httpx.post(
+        **kwargs,
+    )
+
+    return _build_response(response=response)
+
+
+async def asyncio_detailed(
+    *,
+    client: Client,
+    form_data: AFormData,
+) -> Response[None]:
+    kwargs = _get_kwargs(
+        client=client,
+        form_data=form_data,
+    )
+
+    async with httpx.AsyncClient() as _client:
+        response = await _client.post(**kwargs)
+
+    return _build_response(response=response)
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 5bbd77d7d..0efd7dcb6 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,11 @@
 """ Contains all the data models used in inputs/outputs """
 
+from .a_form_data import AFormData
 from .a_model import AModel
+from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject
+from .a_model_with_indirect_reference_property import AModelWithIndirectReferenceProperty
+from .a_model_with_indirect_self_reference_property import AModelWithIndirectSelfReferenceProperty
+from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject
 from .all_of_sub_model import AllOfSubModel
 from .an_all_of_enum import AnAllOfEnum
 from .an_enum import AnEnum
diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py
new file mode 100644
index 000000000..f5c34f5be
--- /dev/null
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_form_data.py
@@ -0,0 +1,63 @@
+from typing import Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..types import UNSET, Unset
+
+T = TypeVar("T", bound="AFormData")
+
+
+@attr.s(auto_attribs=True)
+class AFormData:
+    """ """
+
+    an_required_field: str
+    an_optional_field: Union[Unset, str] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        an_required_field = self.an_required_field
+        an_optional_field = self.an_optional_field
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update(
+            {
+                "an_required_field": an_required_field,
+            }
+        )
+        if an_optional_field is not UNSET:
+            field_dict["an_optional_field"] = an_optional_field
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        an_required_field = d.pop("an_required_field")
+
+        an_optional_field = d.pop("an_optional_field", UNSET)
+
+        a_form_data = cls(
+            an_required_field=an_required_field,
+            an_optional_field=an_optional_field,
+        )
+
+        a_form_data.additional_properties = d
+        return a_form_data
+
+    @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.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model.py
index 38ce8d114..0e2c06d83 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
@@ -186,7 +186,6 @@ def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
 
         def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.datetime]:
             try:
-                a_camel_date_time_type_0: datetime.datetime
                 if not isinstance(data, str):
                     raise TypeError()
                 a_camel_date_time_type_0 = isoparse(data)
@@ -196,7 +195,6 @@ def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.date
                 pass
             if not isinstance(data, str):
                 raise TypeError()
-            a_camel_date_time_type_1: datetime.date
             a_camel_date_time_type_1 = isoparse(data).date()
 
             return a_camel_date_time_type_1
@@ -209,7 +207,6 @@ def _parse_a_camel_date_time(data: object) -> Union[datetime.date, datetime.date
 
         def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionProperty]:
             try:
-                one_of_models_type_0: FreeFormModel
                 if not isinstance(data, dict):
                     raise TypeError()
                 one_of_models_type_0 = FreeFormModel.from_dict(data)
@@ -219,7 +216,6 @@ def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionPro
                 pass
             if not isinstance(data, dict):
                 raise TypeError()
-            one_of_models_type_1: ModelWithUnionProperty
             one_of_models_type_1 = ModelWithUnionProperty.from_dict(data)
 
             return one_of_models_type_1
@@ -228,9 +224,11 @@ def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionPro
 
         model = ModelWithUnionProperty.from_dict(d.pop("model"))
 
-        an_optional_allof_enum: Union[Unset, AnAllOfEnum] = UNSET
         _an_optional_allof_enum = d.pop("an_optional_allof_enum", UNSET)
-        if not isinstance(_an_optional_allof_enum, Unset):
+        an_optional_allof_enum: Union[Unset, AnAllOfEnum]
+        if isinstance(_an_optional_allof_enum, Unset):
+            an_optional_allof_enum = UNSET
+        else:
             an_optional_allof_enum = AnAllOfEnum(_an_optional_allof_enum)
 
         nested_list_of_enums = []
@@ -245,14 +243,18 @@ def _parse_one_of_models(data: object) -> Union[FreeFormModel, ModelWithUnionPro
 
             nested_list_of_enums.append(nested_list_of_enums_item)
 
-        a_nullable_date = None
         _a_nullable_date = d.pop("a_nullable_date")
-        if _a_nullable_date is not None:
+        a_nullable_date: Optional[datetime.date]
+        if _a_nullable_date is None:
+            a_nullable_date = None
+        else:
             a_nullable_date = isoparse(_a_nullable_date).date()
 
-        a_not_required_date: Union[Unset, datetime.date] = UNSET
         _a_not_required_date = d.pop("a_not_required_date", UNSET)
-        if not isinstance(_a_not_required_date, Unset):
+        a_not_required_date: Union[Unset, datetime.date]
+        if isinstance(_a_not_required_date, Unset):
+            a_not_required_date = UNSET
+        else:
             a_not_required_date = isoparse(_a_not_required_date).date()
 
         attr_1_leading_digit = d.pop("1_leading_digit", UNSET)
@@ -267,7 +269,6 @@ def _parse_nullable_one_of_models(data: object) -> Union[FreeFormModel, ModelWit
             if data is None:
                 return data
             try:
-                nullable_one_of_models_type_0: FreeFormModel
                 if not isinstance(data, dict):
                     raise TypeError()
                 nullable_one_of_models_type_0 = FreeFormModel.from_dict(data)
@@ -277,7 +278,6 @@ def _parse_nullable_one_of_models(data: object) -> Union[FreeFormModel, ModelWit
                 pass
             if not isinstance(data, dict):
                 raise TypeError()
-            nullable_one_of_models_type_1: ModelWithUnionProperty
             nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(data)
 
             return nullable_one_of_models_type_1
@@ -288,12 +288,13 @@ def _parse_not_required_one_of_models(data: object) -> Union[FreeFormModel, Mode
             if isinstance(data, Unset):
                 return data
             try:
-                not_required_one_of_models_type_0: Union[Unset, FreeFormModel]
                 if not isinstance(data, dict):
                     raise TypeError()
-                not_required_one_of_models_type_0 = UNSET
                 _not_required_one_of_models_type_0 = data
-                if not isinstance(_not_required_one_of_models_type_0, Unset):
+                not_required_one_of_models_type_0: Union[Unset, FreeFormModel]
+                if isinstance(_not_required_one_of_models_type_0, Unset):
+                    not_required_one_of_models_type_0 = UNSET
+                else:
                     not_required_one_of_models_type_0 = FreeFormModel.from_dict(_not_required_one_of_models_type_0)
 
                 return not_required_one_of_models_type_0
@@ -301,10 +302,11 @@ def _parse_not_required_one_of_models(data: object) -> Union[FreeFormModel, Mode
                 pass
             if not isinstance(data, dict):
                 raise TypeError()
-            not_required_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
-            not_required_one_of_models_type_1 = UNSET
             _not_required_one_of_models_type_1 = data
-            if not isinstance(_not_required_one_of_models_type_1, Unset):
+            not_required_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
+            if isinstance(_not_required_one_of_models_type_1, Unset):
+                not_required_one_of_models_type_1 = UNSET
+            else:
                 not_required_one_of_models_type_1 = ModelWithUnionProperty.from_dict(_not_required_one_of_models_type_1)
 
             return not_required_one_of_models_type_1
@@ -319,12 +321,13 @@ def _parse_not_required_nullable_one_of_models(
             if isinstance(data, Unset):
                 return data
             try:
-                not_required_nullable_one_of_models_type_0: Union[Unset, FreeFormModel]
                 if not isinstance(data, dict):
                     raise TypeError()
-                not_required_nullable_one_of_models_type_0 = UNSET
                 _not_required_nullable_one_of_models_type_0 = data
-                if not isinstance(_not_required_nullable_one_of_models_type_0, Unset):
+                not_required_nullable_one_of_models_type_0: Union[Unset, FreeFormModel]
+                if isinstance(_not_required_nullable_one_of_models_type_0, Unset):
+                    not_required_nullable_one_of_models_type_0 = UNSET
+                else:
                     not_required_nullable_one_of_models_type_0 = FreeFormModel.from_dict(
                         _not_required_nullable_one_of_models_type_0
                     )
@@ -333,12 +336,13 @@ def _parse_not_required_nullable_one_of_models(
             except:  # noqa: E722
                 pass
             try:
-                not_required_nullable_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
                 if not isinstance(data, dict):
                     raise TypeError()
-                not_required_nullable_one_of_models_type_1 = UNSET
                 _not_required_nullable_one_of_models_type_1 = data
-                if not isinstance(_not_required_nullable_one_of_models_type_1, Unset):
+                not_required_nullable_one_of_models_type_1: Union[Unset, ModelWithUnionProperty]
+                if isinstance(_not_required_nullable_one_of_models_type_1, Unset):
+                    not_required_nullable_one_of_models_type_1 = UNSET
+                else:
                     not_required_nullable_one_of_models_type_1 = ModelWithUnionProperty.from_dict(
                         _not_required_nullable_one_of_models_type_1
                     )
@@ -352,19 +356,27 @@ def _parse_not_required_nullable_one_of_models(
             d.pop("not_required_nullable_one_of_models", UNSET)
         )
 
-        nullable_model = None
         _nullable_model = d.pop("nullable_model")
-        if _nullable_model is not None:
+        nullable_model: Optional[ModelWithUnionProperty]
+        if _nullable_model is None:
+            nullable_model = None
+        else:
             nullable_model = ModelWithUnionProperty.from_dict(_nullable_model)
 
-        not_required_model: Union[Unset, ModelWithUnionProperty] = UNSET
         _not_required_model = d.pop("not_required_model", UNSET)
-        if not isinstance(_not_required_model, Unset):
+        not_required_model: Union[Unset, ModelWithUnionProperty]
+        if isinstance(_not_required_model, Unset):
+            not_required_model = UNSET
+        else:
             not_required_model = ModelWithUnionProperty.from_dict(_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: Union[Unset, None, ModelWithUnionProperty]
+        if _not_required_nullable_model is None:
+            not_required_nullable_model = None
+        elif isinstance(_not_required_nullable_model, Unset):
+            not_required_nullable_model = UNSET
+        else:
             not_required_nullable_model = ModelWithUnionProperty.from_dict(_not_required_nullable_model)
 
         a_model = cls(
diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_indirect_reference_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_indirect_reference_property.py
new file mode 100644
index 000000000..14058761d
--- /dev/null
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_indirect_reference_property.py
@@ -0,0 +1,60 @@
+from typing import Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..models.an_enum import AnEnum
+from ..types import UNSET, Unset
+
+T = TypeVar("T", bound="AModelWithIndirectReferenceProperty")
+
+
+@attr.s(auto_attribs=True)
+class AModelWithIndirectReferenceProperty:
+    """ """
+
+    an_enum_indirect_ref: Union[Unset, AnEnum] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        an_enum_indirect_ref: Union[Unset, str] = UNSET
+        if not isinstance(self.an_enum_indirect_ref, Unset):
+            an_enum_indirect_ref = self.an_enum_indirect_ref.value
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update({})
+        if an_enum_indirect_ref is not UNSET:
+            field_dict["an_enum_indirect_ref"] = an_enum_indirect_ref
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        an_enum_indirect_ref: Union[Unset, AnEnum] = UNSET
+        _an_enum_indirect_ref = d.pop("an_enum_indirect_ref", UNSET)
+        if not isinstance(_an_enum_indirect_ref, Unset):
+            an_enum_indirect_ref = AnEnum(_an_enum_indirect_ref)
+
+        a_model_with_indirect_reference_property = cls(
+            an_enum_indirect_ref=an_enum_indirect_ref,
+        )
+
+        a_model_with_indirect_reference_property.additional_properties = d
+        return a_model_with_indirect_reference_property
+
+    @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_with_indirect_self_reference_property.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_indirect_self_reference_property.py
new file mode 100644
index 000000000..2c101f264
--- /dev/null
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_indirect_self_reference_property.py
@@ -0,0 +1,77 @@
+from typing import Any, Dict, List, Type, TypeVar, Union
+
+import attr
+
+from ..models.an_enum import AnEnum
+from ..types import UNSET, Unset
+
+T = TypeVar("T", bound="AModelWithIndirectSelfReferenceProperty")
+
+
+@attr.s(auto_attribs=True)
+class AModelWithIndirectSelfReferenceProperty:
+    """ """
+
+    required_self_ref: AModelWithIndirectSelfReferenceProperty
+    an_enum: Union[Unset, AnEnum] = UNSET
+    optional_self_ref: Union[Unset, AModelWithIndirectSelfReferenceProperty] = UNSET
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        required_self_ref = self.required_self_ref
+        an_enum: Union[Unset, str] = UNSET
+        if not isinstance(self.an_enum, Unset):
+            an_enum = self.an_enum.value
+
+        optional_self_ref = self.optional_self_ref
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update(
+            {
+                "required_self_ref": required_self_ref,
+            }
+        )
+        if an_enum is not UNSET:
+            field_dict["an_enum"] = an_enum
+        if optional_self_ref is not UNSET:
+            field_dict["optional_self_ref"] = optional_self_ref
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        required_self_ref = d.pop("required_self_ref")
+
+        an_enum: Union[Unset, AnEnum] = UNSET
+        _an_enum = d.pop("an_enum", UNSET)
+        if not isinstance(_an_enum, Unset):
+            an_enum = AnEnum(_an_enum)
+
+        optional_self_ref = d.pop("optional_self_ref", UNSET)
+
+        a_model_with_indirect_self_reference_property = cls(
+            required_self_ref=required_self_ref,
+            an_enum=an_enum,
+            optional_self_ref=optional_self_ref,
+        )
+
+        a_model_with_indirect_self_reference_property.additional_properties = d
+        return a_model_with_indirect_self_reference_property
+
+    @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_with_properties_reference_that_are_not_object.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py
new file mode 100644
index 000000000..4b95ec80e
--- /dev/null
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_properties_reference_that_are_not_object.py
@@ -0,0 +1,341 @@
+import datetime
+from io import BytesIO
+from typing import Any, BinaryIO, Dict, List, Optional, TextIO, Tuple, Type, TypeVar, Union, cast
+
+import attr
+from dateutil.parser import isoparse
+
+from ..models.an_enum import AnEnum
+from ..types import File
+
+T = TypeVar("T", bound="AModelWithPropertiesReferenceThatAreNotObject")
+
+
+@attr.s(auto_attribs=True)
+class AModelWithPropertiesReferenceThatAreNotObject:
+    """ """
+
+    enum_properties_ref: List[AnEnum]
+    str_properties_ref: List[str]
+    date_properties_ref: List[datetime.date]
+    datetime_properties_ref: List[datetime.datetime]
+    int_32_properties_ref: List[int]
+    int_64_properties_ref: List[int]
+    float_properties_ref: List[float]
+    double_properties_ref: List[float]
+    file_properties_ref: List[File]
+    bytestream_properties_ref: List[str]
+    enum_properties: List[AnEnum]
+    str_properties: List[str]
+    date_properties: List[datetime.date]
+    datetime_properties: List[datetime.datetime]
+    int_32_properties: List[int]
+    int_64_properties: List[int]
+    float_properties: List[float]
+    double_properties: List[float]
+    file_properties: List[File]
+    bytestream_properties: List[str]
+    enum_property_ref: AnEnum
+    str_property_ref: str
+    date_property_ref: datetime.date
+    datetime_property_ref: datetime.datetime
+    int_32_property_ref: int
+    int_64_property_ref: int
+    float_property_ref: float
+    double_property_ref: float
+    file_property_ref: File
+    bytestream_property_ref: str
+    additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
+
+    def to_dict(self) -> Dict[str, Any]:
+        enum_properties_ref = []
+        for componentsschemas_an_other_array_of_enum_item_data in self.enum_properties_ref:
+            componentsschemas_an_other_array_of_enum_item = componentsschemas_an_other_array_of_enum_item_data.value
+
+            enum_properties_ref.append(componentsschemas_an_other_array_of_enum_item)
+
+        str_properties_ref = self.str_properties_ref
+
+        date_properties_ref = []
+        for componentsschemas_an_other_array_of_date_item_data in self.date_properties_ref:
+            componentsschemas_an_other_array_of_date_item = (
+                componentsschemas_an_other_array_of_date_item_data.isoformat()
+            )
+            date_properties_ref.append(componentsschemas_an_other_array_of_date_item)
+
+        datetime_properties_ref = []
+        for componentsschemas_an_other_array_of_date_time_item_data in self.datetime_properties_ref:
+            componentsschemas_an_other_array_of_date_time_item = (
+                componentsschemas_an_other_array_of_date_time_item_data.isoformat()
+            )
+
+            datetime_properties_ref.append(componentsschemas_an_other_array_of_date_time_item)
+
+        int_32_properties_ref = self.int_32_properties_ref
+
+        int_64_properties_ref = self.int_64_properties_ref
+
+        float_properties_ref = self.float_properties_ref
+
+        double_properties_ref = self.double_properties_ref
+
+        file_properties_ref = []
+        for componentsschemas_an_other_array_of_file_item_data in self.file_properties_ref:
+            componentsschemas_an_other_array_of_file_item = (
+                componentsschemas_an_other_array_of_file_item_data.to_tuple()
+            )
+
+            file_properties_ref.append(componentsschemas_an_other_array_of_file_item)
+
+        bytestream_properties_ref = self.bytestream_properties_ref
+
+        enum_properties = []
+        for componentsschemas_an_array_of_enum_item_data in self.enum_properties:
+            componentsschemas_an_array_of_enum_item = componentsschemas_an_array_of_enum_item_data.value
+
+            enum_properties.append(componentsschemas_an_array_of_enum_item)
+
+        str_properties = self.str_properties
+
+        date_properties = []
+        for componentsschemas_an_array_of_date_item_data in self.date_properties:
+            componentsschemas_an_array_of_date_item = componentsschemas_an_array_of_date_item_data.isoformat()
+            date_properties.append(componentsschemas_an_array_of_date_item)
+
+        datetime_properties = []
+        for componentsschemas_an_array_of_date_time_item_data in self.datetime_properties:
+            componentsschemas_an_array_of_date_time_item = componentsschemas_an_array_of_date_time_item_data.isoformat()
+
+            datetime_properties.append(componentsschemas_an_array_of_date_time_item)
+
+        int_32_properties = self.int_32_properties
+
+        int_64_properties = self.int_64_properties
+
+        float_properties = self.float_properties
+
+        double_properties = self.double_properties
+
+        file_properties = []
+        for componentsschemas_an_array_of_file_item_data in self.file_properties:
+            componentsschemas_an_array_of_file_item = componentsschemas_an_array_of_file_item_data.to_tuple()
+
+            file_properties.append(componentsschemas_an_array_of_file_item)
+
+        bytestream_properties = self.bytestream_properties
+
+        enum_property_ref = self.enum_property_ref.value
+
+        str_property_ref = self.str_property_ref
+        date_property_ref = self.date_property_ref.isoformat()
+        datetime_property_ref = self.datetime_property_ref.isoformat()
+
+        int_32_property_ref = self.int_32_property_ref
+        int_64_property_ref = self.int_64_property_ref
+        float_property_ref = self.float_property_ref
+        double_property_ref = self.double_property_ref
+        file_property_ref = self.file_property_ref.to_tuple()
+
+        bytestream_property_ref = self.bytestream_property_ref
+
+        field_dict: Dict[str, Any] = {}
+        field_dict.update(self.additional_properties)
+        field_dict.update(
+            {
+                "enum_properties_ref": enum_properties_ref,
+                "str_properties_ref": str_properties_ref,
+                "date_properties_ref": date_properties_ref,
+                "datetime_properties_ref": datetime_properties_ref,
+                "int32_properties_ref": int_32_properties_ref,
+                "int64_properties_ref": int_64_properties_ref,
+                "float_properties_ref": float_properties_ref,
+                "double_properties_ref": double_properties_ref,
+                "file_properties_ref": file_properties_ref,
+                "bytestream_properties_ref": bytestream_properties_ref,
+                "enum_properties": enum_properties,
+                "str_properties": str_properties,
+                "date_properties": date_properties,
+                "datetime_properties": datetime_properties,
+                "int32_properties": int_32_properties,
+                "int64_properties": int_64_properties,
+                "float_properties": float_properties,
+                "double_properties": double_properties,
+                "file_properties": file_properties,
+                "bytestream_properties": bytestream_properties,
+                "enum_property_ref": enum_property_ref,
+                "str_property_ref": str_property_ref,
+                "date_property_ref": date_property_ref,
+                "datetime_property_ref": datetime_property_ref,
+                "int32_property_ref": int_32_property_ref,
+                "int64_property_ref": int_64_property_ref,
+                "float_property_ref": float_property_ref,
+                "double_property_ref": double_property_ref,
+                "file_property_ref": file_property_ref,
+                "bytestream_property_ref": bytestream_property_ref,
+            }
+        )
+
+        return field_dict
+
+    @classmethod
+    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
+        d = src_dict.copy()
+        enum_properties_ref = []
+        _enum_properties_ref = d.pop("enum_properties_ref")
+        for componentsschemas_an_other_array_of_enum_item_data in _enum_properties_ref:
+            componentsschemas_an_other_array_of_enum_item = AnEnum(componentsschemas_an_other_array_of_enum_item_data)
+
+            enum_properties_ref.append(componentsschemas_an_other_array_of_enum_item)
+
+        str_properties_ref = cast(List[str], d.pop("str_properties_ref"))
+
+        date_properties_ref = []
+        _date_properties_ref = d.pop("date_properties_ref")
+        for componentsschemas_an_other_array_of_date_item_data in _date_properties_ref:
+            componentsschemas_an_other_array_of_date_item = isoparse(
+                componentsschemas_an_other_array_of_date_item_data
+            ).date()
+
+            date_properties_ref.append(componentsschemas_an_other_array_of_date_item)
+
+        datetime_properties_ref = []
+        _datetime_properties_ref = d.pop("datetime_properties_ref")
+        for componentsschemas_an_other_array_of_date_time_item_data in _datetime_properties_ref:
+            componentsschemas_an_other_array_of_date_time_item = isoparse(
+                componentsschemas_an_other_array_of_date_time_item_data
+            )
+
+            datetime_properties_ref.append(componentsschemas_an_other_array_of_date_time_item)
+
+        int_32_properties_ref = cast(List[int], d.pop("int32_properties_ref"))
+
+        int_64_properties_ref = cast(List[int], d.pop("int64_properties_ref"))
+
+        float_properties_ref = cast(List[float], d.pop("float_properties_ref"))
+
+        double_properties_ref = cast(List[float], d.pop("double_properties_ref"))
+
+        file_properties_ref = []
+        _file_properties_ref = d.pop("file_properties_ref")
+        for componentsschemas_an_other_array_of_file_item_data in _file_properties_ref:
+            componentsschemas_an_other_array_of_file_item = File(
+                payload=BytesIO(componentsschemas_an_other_array_of_file_item_data)
+            )
+
+            file_properties_ref.append(componentsschemas_an_other_array_of_file_item)
+
+        bytestream_properties_ref = cast(List[str], d.pop("bytestream_properties_ref"))
+
+        enum_properties = []
+        _enum_properties = d.pop("enum_properties")
+        for componentsschemas_an_array_of_enum_item_data in _enum_properties:
+            componentsschemas_an_array_of_enum_item = AnEnum(componentsschemas_an_array_of_enum_item_data)
+
+            enum_properties.append(componentsschemas_an_array_of_enum_item)
+
+        str_properties = cast(List[str], d.pop("str_properties"))
+
+        date_properties = []
+        _date_properties = d.pop("date_properties")
+        for componentsschemas_an_array_of_date_item_data in _date_properties:
+            componentsschemas_an_array_of_date_item = isoparse(componentsschemas_an_array_of_date_item_data).date()
+
+            date_properties.append(componentsschemas_an_array_of_date_item)
+
+        datetime_properties = []
+        _datetime_properties = d.pop("datetime_properties")
+        for componentsschemas_an_array_of_date_time_item_data in _datetime_properties:
+            componentsschemas_an_array_of_date_time_item = isoparse(componentsschemas_an_array_of_date_time_item_data)
+
+            datetime_properties.append(componentsschemas_an_array_of_date_time_item)
+
+        int_32_properties = cast(List[int], d.pop("int32_properties"))
+
+        int_64_properties = cast(List[int], d.pop("int64_properties"))
+
+        float_properties = cast(List[float], d.pop("float_properties"))
+
+        double_properties = cast(List[float], d.pop("double_properties"))
+
+        file_properties = []
+        _file_properties = d.pop("file_properties")
+        for componentsschemas_an_array_of_file_item_data in _file_properties:
+            componentsschemas_an_array_of_file_item = File(
+                payload=BytesIO(componentsschemas_an_array_of_file_item_data)
+            )
+
+            file_properties.append(componentsschemas_an_array_of_file_item)
+
+        bytestream_properties = cast(List[str], d.pop("bytestream_properties"))
+
+        enum_property_ref = AnEnum(d.pop("enum_property_ref"))
+
+        str_property_ref = d.pop("str_property_ref")
+
+        date_property_ref = isoparse(d.pop("date_property_ref")).date()
+
+        datetime_property_ref = isoparse(d.pop("datetime_property_ref"))
+
+        int_32_property_ref = d.pop("int32_property_ref")
+
+        int_64_property_ref = d.pop("int64_property_ref")
+
+        float_property_ref = d.pop("float_property_ref")
+
+        double_property_ref = d.pop("double_property_ref")
+
+        file_property_ref = File(payload=BytesIO(d.pop("file_property_ref")))
+
+        bytestream_property_ref = d.pop("bytestream_property_ref")
+
+        a_model_with_properties_reference_that_are_not_object = cls(
+            enum_properties_ref=enum_properties_ref,
+            str_properties_ref=str_properties_ref,
+            date_properties_ref=date_properties_ref,
+            datetime_properties_ref=datetime_properties_ref,
+            int_32_properties_ref=int_32_properties_ref,
+            int_64_properties_ref=int_64_properties_ref,
+            float_properties_ref=float_properties_ref,
+            double_properties_ref=double_properties_ref,
+            file_properties_ref=file_properties_ref,
+            bytestream_properties_ref=bytestream_properties_ref,
+            enum_properties=enum_properties,
+            str_properties=str_properties,
+            date_properties=date_properties,
+            datetime_properties=datetime_properties,
+            int_32_properties=int_32_properties,
+            int_64_properties=int_64_properties,
+            float_properties=float_properties,
+            double_properties=double_properties,
+            file_properties=file_properties,
+            bytestream_properties=bytestream_properties,
+            enum_property_ref=enum_property_ref,
+            str_property_ref=str_property_ref,
+            date_property_ref=date_property_ref,
+            datetime_property_ref=datetime_property_ref,
+            int_32_property_ref=int_32_property_ref,
+            int_64_property_ref=int_64_property_ref,
+            float_property_ref=float_property_ref,
+            double_property_ref=double_property_ref,
+            file_property_ref=file_property_ref,
+            bytestream_property_ref=bytestream_property_ref,
+        )
+
+        a_model_with_properties_reference_that_are_not_object.additional_properties = d
+        return a_model_with_properties_reference_that_are_not_object
+
+    @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_any_json_properties.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py
index ecfa97b10..08a016dd8 100644
--- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_any_json_properties.py
@@ -46,7 +46,6 @@ def _parse_additional_property(
                 data: object,
             ) -> Union[List[str], ModelWithAnyJsonPropertiesAdditionalPropertyType0, bool, float, int, str]:
                 try:
-                    additional_property_type_0: ModelWithAnyJsonPropertiesAdditionalPropertyType0
                     if not isinstance(data, dict):
                         raise TypeError()
                     additional_property_type_0 = ModelWithAnyJsonPropertiesAdditionalPropertyType0.from_dict(data)
@@ -55,7 +54,6 @@ def _parse_additional_property(
                 except:  # noqa: E722
                     pass
                 try:
-                    additional_property_type_1: List[str]
                     if not isinstance(data, list):
                         raise TypeError()
                     additional_property_type_1 = cast(List[str], data)
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 5dc264152..ee28313bd 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,9 +33,11 @@ def to_dict(self) -> Dict[str, Any]:
     @classmethod
     def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
         d = src_dict.copy()
-        a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder] = UNSET
         _a_date_holder = d.pop("a_date_holder", UNSET)
-        if not isinstance(_a_date_holder, Unset):
+        a_date_holder: Union[Unset, ModelWithPrimitiveAdditionalPropertiesADateHolder]
+        if isinstance(_a_date_holder, Unset):
+            a_date_holder = UNSET
+        else:
             a_date_holder = ModelWithPrimitiveAdditionalPropertiesADateHolder.from_dict(_a_date_holder)
 
         model_with_primitive_additional_properties = cls(
diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py
index 6ebba75a6..e28a14e91 100644
--- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_property_ref.py
@@ -31,9 +31,11 @@ def to_dict(self) -> Dict[str, Any]:
     @classmethod
     def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
         d = src_dict.copy()
-        inner: Union[Unset, ModelName] = UNSET
         _inner = d.pop("inner", UNSET)
-        if not isinstance(_inner, Unset):
+        inner: Union[Unset, ModelName]
+        if isinstance(_inner, Unset):
+            inner = UNSET
+        else:
             inner = ModelName.from_dict(_inner)
 
         model_with_property_ref = cls(
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 87034d5e7..b7fa116e3 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
@@ -44,12 +44,13 @@ def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]:
             if isinstance(data, Unset):
                 return data
             try:
-                a_property_type_0: Union[Unset, AnEnum]
                 if not isinstance(data, str):
                     raise TypeError()
-                a_property_type_0 = UNSET
                 _a_property_type_0 = data
-                if not isinstance(_a_property_type_0, Unset):
+                a_property_type_0: Union[Unset, AnEnum]
+                if isinstance(_a_property_type_0, Unset):
+                    a_property_type_0 = UNSET
+                else:
                     a_property_type_0 = AnEnum(_a_property_type_0)
 
                 return a_property_type_0
@@ -57,10 +58,11 @@ def _parse_a_property(data: object) -> Union[AnEnum, AnIntEnum, Unset]:
                 pass
             if not isinstance(data, int):
                 raise TypeError()
-            a_property_type_1: Union[Unset, AnIntEnum]
-            a_property_type_1 = UNSET
             _a_property_type_1 = data
-            if not isinstance(_a_property_type_1, Unset):
+            a_property_type_1: Union[Unset, AnIntEnum]
+            if isinstance(_a_property_type_1, Unset):
+                a_property_type_1 = UNSET
+            else:
                 a_property_type_1 = AnIntEnum(_a_property_type_1)
 
             return a_property_type_1
diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py
index 822f45cc2..27e91a80a 100644
--- a/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py
+++ b/end_to_end_tests/golden-record/my_test_api_client/models/model_with_union_property_inlined.py
@@ -46,12 +46,13 @@ def _parse_fruit(
             if isinstance(data, Unset):
                 return data
             try:
-                fruit_type_0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0]
                 if not isinstance(data, dict):
                     raise TypeError()
-                fruit_type_0 = UNSET
                 _fruit_type_0 = data
-                if not isinstance(_fruit_type_0, Unset):
+                fruit_type_0: Union[Unset, ModelWithUnionPropertyInlinedFruitType0]
+                if isinstance(_fruit_type_0, Unset):
+                    fruit_type_0 = UNSET
+                else:
                     fruit_type_0 = ModelWithUnionPropertyInlinedFruitType0.from_dict(_fruit_type_0)
 
                 return fruit_type_0
@@ -59,10 +60,11 @@ def _parse_fruit(
                 pass
             if not isinstance(data, dict):
                 raise TypeError()
-            fruit_type_1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1]
-            fruit_type_1 = UNSET
             _fruit_type_1 = data
-            if not isinstance(_fruit_type_1, Unset):
+            fruit_type_1: Union[Unset, ModelWithUnionPropertyInlinedFruitType1]
+            if isinstance(_fruit_type_1, Unset):
+                fruit_type_1 = UNSET
+            else:
                 fruit_type_1 = ModelWithUnionPropertyInlinedFruitType1.from_dict(_fruit_type_1)
 
             return fruit_type_1
diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json
index 585b97cde..094bd920a 100644
--- a/end_to_end_tests/openapi.json
+++ b/end_to_end_tests/openapi.json
@@ -188,6 +188,36 @@
         }
       }
     },
+    "/tests/post_form_data": {
+      "post": {
+        "tags": [
+          "tests"
+        ],
+        "sumnary": "Post from data",
+        "description": "Post form data",
+        "operationId": "post_form_data",
+        "requestBody": {
+          "content": {
+            "application/x-www-form-urlencoded": {
+              "schema": {
+                "$ref": "#/components/schemas/AFormData"
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "description": "Successful Response",
+            "content": {
+              "application/json": {
+                "schema": {}
+              }
+            }
+          }
+        }
+      }
+    },
     "/tests/upload": {
       "post": {
         "tags": [
@@ -752,19 +782,25 @@
       ],
       "get": {
         "responses": {
-          "200": {"description": "Success"}
+          "200": {
+            "description": "Success"
+          }
         }
       },
       "post": {
         "responses": {
-          "200": {"description": "Success"}
+          "200": {
+            "description": "Success"
+          }
         }
       }
     },
     "/same-name-multiple-locations/{param}": {
       "description": "Test that if you have a property of the same name in multiple locations, it produces valid code",
       "get": {
-        "tags": ["parameters"],
+        "tags": [
+          "parameters"
+        ],
         "parameters": [
           {
             "name": "param",
@@ -791,6 +827,20 @@
   },
   "components": {
     "schemas": {
+      "AFormData": {
+        "type": "object",
+        "properties": {
+          "an_optional_field": {
+            "type": "string"
+          },
+          "an_required_field": {
+            "type": "string"
+          }
+        },
+        "required": [
+          "an_required_field"
+        ]
+      },
       "AModel": {
         "title": "AModel",
         "required": [
@@ -893,10 +943,10 @@
           "one_of_models": {
             "oneOf": [
               {
-                "ref": "#/components/schemas/FreeFormModel"
+                "$ref": "#/components/schemas/FreeFormModel"
               },
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": false
@@ -904,10 +954,10 @@
           "nullable_one_of_models": {
             "oneOf": [
               {
-                "ref": "#/components/schemas/FreeFormModel"
+                "$ref": "#/components/schemas/FreeFormModel"
               },
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": true
@@ -915,10 +965,10 @@
           "not_required_one_of_models": {
             "oneOf": [
               {
-                "ref": "#/components/schemas/FreeFormModel"
+                "$ref": "#/components/schemas/FreeFormModel"
               },
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": false
@@ -926,10 +976,10 @@
           "not_required_nullable_one_of_models": {
             "oneOf": [
               {
-                "ref": "#/components/schemas/FreeFormModel"
+                "$ref": "#/components/schemas/FreeFormModel"
               },
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               },
               {
                 "type": "string"
@@ -940,7 +990,7 @@
           "model": {
             "allOf": [
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": false
@@ -948,7 +998,7 @@
           "nullable_model": {
             "allOf": [
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": true
@@ -956,7 +1006,7 @@
           "not_required_model": {
             "allOf": [
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": false
@@ -964,7 +1014,7 @@
           "not_required_nullable_model": {
             "allOf": [
               {
-                "ref": "#/components/schemas/ModelWithUnionProperty"
+                "$ref": "#/components/schemas/ModelWithUnionProperty"
               }
             ],
             "nullable": true
@@ -975,23 +1025,37 @@
       },
       "AnEnum": {
         "title": "AnEnum",
-        "enum": ["FIRST_VALUE", "SECOND_VALUE"],
+        "enum": [
+          "FIRST_VALUE",
+          "SECOND_VALUE"
+        ],
         "description": "For testing Enums in all the ways they can be used "
       },
       "AnAllOfEnum": {
         "title": "AnAllOfEnum",
-        "enum": ["foo", "bar", "a_default", "overridden_default"],
+        "enum": [
+          "foo",
+          "bar",
+          "a_default",
+          "overridden_default"
+        ],
         "default": "a_default"
       },
       "AnIntEnum": {
         "title": "AnIntEnum",
-        "enum": [-1, 1, 2],
+        "enum": [
+          -1,
+          1,
+          2
+        ],
         "type": "integer",
         "description": "An enumeration."
       },
       "Body_upload_file_tests_upload_post": {
         "title": "Body_upload_file_tests_upload_post",
-        "required": ["some_file"],
+        "required": [
+          "some_file"
+        ],
         "type": "object",
         "properties": {
           "some_file": {
@@ -1009,7 +1073,10 @@
       },
       "DifferentEnum": {
         "title": "DifferentEnum",
-        "enum": ["DIFFERENT", "OTHER"],
+        "enum": [
+          "DIFFERENT",
+          "OTHER"
+        ],
         "description": "An enumeration."
       },
       "HTTPValidationError": {
@@ -1028,7 +1095,11 @@
       },
       "ValidationError": {
         "title": "ValidationError",
-        "required": ["loc", "msg", "type"],
+        "required": [
+          "loc",
+          "msg",
+          "type"
+        ],
         "type": "object",
         "properties": {
           "loc": {
@@ -1055,8 +1126,12 @@
         "properties": {
           "a_property": {
             "oneOf": [
-              {"$ref": "#/components/schemas/AnEnum"},
-              {"$ref": "#/components/schemas/AnIntEnum"}
+              {
+                "$ref": "#/components/schemas/AnEnum"
+              },
+              {
+                "$ref": "#/components/schemas/AnIntEnum"
+              }
             ]
           }
         },
@@ -1071,13 +1146,17 @@
               {
                 "type": "object",
                 "properties": {
-                  "apples": {"type": "string"}
+                  "apples": {
+                    "type": "string"
+                  }
                 }
               },
               {
                 "type": "object",
                 "properties": {
-                  "bananas": {"type": "string"}
+                  "bananas": {
+                    "type": "string"
+                  }
                 }
               }
             ]
@@ -1099,7 +1178,9 @@
         "additionalProperties": {
           "type": "object",
           "properties": {
-            "extra_props_prop": {"type": "string"}
+            "extra_props_prop": {
+              "type": "string"
+            }
           },
           "additionalProperties": {}
         }
@@ -1163,8 +1244,12 @@
         "title": "ModelFromAllOf",
         "type": "object",
         "allOf": [
-          {"$ref": "#/components/schemas/AllOfSubModel"},
-          {"$ref": "#/components/schemas/AnotherAllOfSubModel"}
+          {
+            "$ref": "#/components/schemas/AllOfSubModel"
+          },
+          {
+            "$ref": "#/components/schemas/AnotherAllOfSubModel"
+          }
         ]
       },
       "AllOfSubModel": {
@@ -1192,8 +1277,605 @@
       "ModelWithPropertyRef": {
         "type": "object",
         "properties": {
-          "inner": {"$ref":  "#/components/schemas/model_reference_doesnt_match"}
+          "inner": {
+            "$ref": "#/components/schemas/model_reference_doesnt_match"
+          }
+        }
+      },
+      "AModelWithPropertiesReferenceThatAreNotObject": {
+        "type": "object",
+        "required": [
+          "enum_properties_ref",
+          "str_properties_ref",
+          "date_properties_ref",
+          "datetime_properties_ref",
+          "int32_properties_ref",
+          "int64_properties_ref",
+          "float_properties_ref",
+          "double_properties_ref",
+          "file_properties_ref",
+          "bytestream_properties_ref",
+          "enum_properties",
+          "str_properties",
+          "date_properties",
+          "datetime_properties",
+          "int32_properties",
+          "int64_properties",
+          "float_properties",
+          "double_properties",
+          "file_properties",
+          "bytestream_properties",
+          "enum_property_ref",
+          "str_property_ref",
+          "date_property_ref",
+          "datetime_property_ref",
+          "int32_property_ref",
+          "int64_property_ref",
+          "float_property_ref",
+          "double_property_ref",
+          "file_property_ref",
+          "bytestream_property_ref"
+        ],
+        "properties": {
+          "enum_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfEnum"
+          },
+          "str_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfString"
+          },
+          "date_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfDate"
+          },
+          "datetime_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfDateTime"
+          },
+          "int32_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfInt32"
+          },
+          "int64_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfInt64"
+          },
+          "float_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfFloat"
+          },
+          "double_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfDouble"
+          },
+          "file_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfFile"
+          },
+          "bytestream_properties_ref": {
+            "$ref": "#/components/schemas/AnOtherArrayOfByteStream"
+          },
+          "enum_properties": {
+            "$ref": "#/components/schemas/AnArrayOfEnum"
+          },
+          "str_properties": {
+            "$ref": "#/components/schemas/AnArrayOfString"
+          },
+          "date_properties": {
+            "$ref": "#/components/schemas/AnArrayOfDate"
+          },
+          "datetime_properties": {
+            "$ref": "#/components/schemas/AnArrayOfDateTime"
+          },
+          "int32_properties": {
+            "$ref": "#/components/schemas/AnArrayOfInt32"
+          },
+          "int64_properties": {
+            "$ref": "#/components/schemas/AnArrayOfInt64"
+          },
+          "float_properties": {
+            "$ref": "#/components/schemas/AnArrayOfFloat"
+          },
+          "double_properties": {
+            "$ref": "#/components/schemas/AnArrayOfDouble"
+          },
+          "file_properties": {
+            "$ref": "#/components/schemas/AnArrayOfFile"
+          },
+          "bytestream_properties": {
+            "$ref": "#/components/schemas/AnArrayOfByteStream"
+          },
+          "enum_property_ref": {
+            "$ref": "#/components/schemas/AnEnum"
+          },
+          "str_property_ref": {
+            "$ref": "#/components/schemas/AString"
+          },
+          "date_property_ref": {
+            "$ref": "#/components/schemas/ADate"
+          },
+          "datetime_property_ref": {
+            "$ref": "#/components/schemas/ADateTime"
+          },
+          "int32_property_ref": {
+            "$ref": "#/components/schemas/AnInt32"
+          },
+          "int64_property_ref": {
+            "$ref": "#/components/schemas/AnInt64"
+          },
+          "float_property_ref": {
+            "$ref": "#/components/schemas/AFloat"
+          },
+          "double_property_ref": {
+            "$ref": "#/components/schemas/ADouble"
+          },
+          "file_property_ref": {
+            "$ref": "#/components/schemas/AFile"
+          },
+          "bytestream_property_ref": {
+            "$ref": "#/components/schemas/AByteStream"
+          }
+        }
+      },
+      "AnArrayOfEnum": {
+        "type": "array",
+        "items": {
+          "title": "AnEnum",
+          "enum": [
+            "FIRST_VALUE",
+            "SECOND_VALUE"
+          ],
+          "description": "For testing Enums in all the ways they can be used "
+        }
+      },
+      "AnOtherArrayOfEnum": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnEnum"
+        }
+      },
+      "AnArrayOfString": {
+        "type": "array",
+        "items": {
+          "type": "string"
+        }
+      },
+      "AnOtherArrayOfString": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AString"
+        }
+      },
+      "AString": {
+        "type": "string",
+        "pattern": "^helloworld.*"
+      },
+      "AnArrayOfDate": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "date"
+        }
+      },
+      "AnOtherArrayOfDate": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADate"
+        }
+      },
+      "ADate": {
+        "type": "string",
+        "format": "date"
+      },
+      "AnArrayOfDateTime": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "date-time"
+        }
+      },
+      "AnOtherArrayOfDateTime": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADateTime"
+        }
+      },
+      "ADateTime": {
+        "type": "string",
+        "format": "date-time"
+      },
+      "AnArrayOfInt32": {
+        "type": "array",
+        "items": {
+          "type": "integer",
+          "format": "int32"
+        }
+      },
+      "AnOtherArrayOfInt32": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnInt32"
+        }
+      },
+      "AnInt32": {
+        "type": "integer",
+        "format": "int32"
+      },
+      "AnArrayOfInt64": {
+        "type": "array",
+        "items": {
+          "type": "integer",
+          "format": "int64"
+        }
+      },
+      "AnOtherArrayOfInt64": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnInt64"
+        }
+      },
+      "AnInt64": {
+        "type": "integer",
+        "format": "int64"
+      },
+      "AnArrayOfFloat": {
+        "type": "array",
+        "items": {
+          "type": "number",
+          "format": "float"
+        }
+      },
+      "AnOtherArrayOfFloat": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AFloat"
+        }
+      },
+      "AFloat": {
+        "type": "number",
+        "format": "float"
+      },
+      "AnArrayOfDouble": {
+        "type": "array",
+        "items": {
+          "type": "number",
+          "format": "float"
+        }
+      },
+      "AnOtherArrayOfDouble": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADouble"
+        }
+      },
+      "ADouble": {
+        "type": "number",
+        "format": "double"
+      },
+      "AnArrayOfFile": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "binary"
+        }
+      },
+      "AnOtherArrayOfFile": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AFile"
+        }
+      },
+      "AFile": {
+        "type": "string",
+        "format": "binary"
+      },
+      "AnArrayOfByteStream": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "byte"
+        }
+      },
+      "AnOtherArrayOfByteStream": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AByteStream"
+        }
+      },
+      "AByteStream": {
+        "type": "string",
+        "format": "byte"
+      },
+      "AModelWithIndirectReferenceProperty": {
+        "title": "AModelWithIndirectReferenceProperty",
+        "type": "object",
+        "properties": {
+          "an_enum_indirect_ref": {
+            "$ref": "#/components/schemas/AnEnumDeeperIndirectReference"
+          }
+        }
+      },
+      "AnEnumDeeperIndirectReference": {
+        "$ref": "#/components/schemas/AnEnumIndirectReference"
+      },
+      "AnEnumIndirectReference": {
+        "$ref": "#/components/schemas/AnEnum"
+      },
+      "AModelWithIndirectSelfReferenceProperty": {
+        "type": "object",
+        "properties": {
+          "an_enum": {
+            "$ref": "#/components/schemas/AnEnum"
+          },
+          "required_self_ref": {
+            "$ref": "#/components/schemas/AnDeeperIndirectSelfReference"
+          },
+          "optional_self_ref": {
+            "$ref": "#/components/schemas/AnDeeperIndirectSelfReference"
+           }
+        },
+        "required": [
+          "required_self_ref"
+        ]
+      },
+      "AnDeeperIndirectSelfReference": {
+        "$ref": "#/components/schemas/AnIndirectSelfReference"
+      },
+      "AnIndirectSelfReference": {
+        "$ref": "#/components/schemas/AModelWithIndirectSelfReferenceProperty"
+      },
+      "AModelWithPropertiesReferenceThatAreNotObject": {
+        "type": "object",
+        "properties": {
+            "enum_properties_ref": {
+              "$ref": "#/components/schemas/AnOtherArrayOfEnum"
+            },
+            "str_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfString"
+            },
+            "date_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfDate"
+            },
+            "datetime_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfDateTime"
+            },
+            "int32_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfInt32"
+            },
+            "int64_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfInt64"
+            },
+            "float_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfFloat"
+            },
+            "double_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfDouble"
+            },
+            "file_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfFile"
+            },
+            "bytestream_properties_ref": {
+                "$ref": "#/components/schemas/AnOtherArrayOfByteStream"
+            },
+            "enum_properties": {
+              "$ref": "#/components/schemas/AnArrayOfEnum"
+            },
+            "str_properties": {
+                "$ref": "#/components/schemas/AnArrayOfString"
+            },
+            "date_properties": {
+                "$ref": "#/components/schemas/AnArrayOfDate"
+            },
+            "datetime_properties": {
+                "$ref": "#/components/schemas/AnArrayOfDateTime"
+            },
+            "int32_properties": {
+                "$ref": "#/components/schemas/AnArrayOfInt32"
+            },
+            "int64_properties": {
+                "$ref": "#/components/schemas/AnArrayOfInt64"
+            },
+            "float_properties": {
+                "$ref": "#/components/schemas/AnArrayOfFloat"
+            },
+            "double_properties": {
+                "$ref": "#/components/schemas/AnArrayOfDouble"
+            },
+            "file_properties": {
+                "$ref": "#/components/schemas/AnArrayOfFile"
+            },
+            "bytestream_properties": {
+                "$ref": "#/components/schemas/AnArrayOfByteStream"
+            },
+            "enum_property_ref": {
+              "$ref": "#/components/schemas/AnEnum"
+            },
+            "str_property_ref": {
+                "$ref": "#/components/schemas/AString"
+            },
+            "date_property_ref": {
+                "$ref": "#/components/schemas/ADate"
+            },
+            "datetime_property_ref": {
+                "$ref": "#/components/schemas/ADateTime"
+            },
+            "int32_property_ref": {
+                "$ref": "#/components/schemas/AnInt32"
+            },
+            "int64_property_ref": {
+                "$ref": "#/components/schemas/AnInt64"
+            },
+            "float_property_ref": {
+                "$ref": "#/components/schemas/AFloat"
+            },
+            "double_property_ref": {
+                "$ref": "#/components/schemas/ADouble"
+            },
+            "file_property_ref": {
+                "$ref": "#/components/schemas/AFile"
+            },
+            "bytestream_property_ref": {
+                "$ref": "#/components/schemas/AByteStream"
+            }
+        }
+      },
+      "AnArrayOfEnum": {
+        "type": "array",
+        "items": {
+          "title": "AnEnum",
+          "enum": ["FIRST_VALUE", "SECOND_VALUE"],
+          "description": "For testing Enums in all the ways they can be used "
+        }
+      },
+      "AnOtherArrayOfEnum": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnEnum"
         }
+      },
+      "AnArrayOfString": {
+        "type": "array",
+        "items": {
+          "type": "string"
+        }
+      },
+      "AnOtherArrayOfString": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AString"
+        }
+      },
+      "AString": {
+        "type": "string",
+        "pattern": "^helloworld.*"
+      },
+      "AnArrayOfDate": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "date"
+        }
+      },
+      "AnOtherArrayOfDate": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADate"
+        }
+      },
+      "ADate": {
+        "type": "string",
+        "format": "date"
+      },
+      "AnArrayOfDateTime": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "date-time"
+        }
+      },
+      "AnOtherArrayOfDateTime": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADateTime"
+        }
+      },
+      "ADateTime": {
+        "type": "string",
+        "format": "date-time"
+      },
+      "AnArrayOfInt32": {
+        "type": "array",
+        "items": {
+          "type": "integer",
+          "format": "int32"
+        }
+      },
+      "AnOtherArrayOfInt32": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnInt32"
+        }
+      },
+      "AnInt32": {
+        "type": "integer",
+        "format": "int32"
+      },
+      "AnArrayOfInt64": {
+        "type": "array",
+        "items": {
+          "type": "integer",
+          "format": "int64"
+        }
+      },
+      "AnOtherArrayOfInt64": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AnInt64"
+        }
+      },
+      "AnInt64": {
+        "type": "integer",
+        "format": "int64"
+      },
+      "AnArrayOfFloat": {
+        "type": "array",
+        "items": {
+          "type": "number",
+          "format": "float"
+        }
+      },
+      "AnOtherArrayOfFloat": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AFloat"
+        }
+      },
+      "AFloat": {
+        "type": "number",
+        "format": "float"
+      },
+      "AnArrayOfDouble": {
+        "type": "array",
+        "items": {
+          "type": "number",
+          "format": "float"
+        }
+      },
+      "AnOtherArrayOfDouble": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/ADouble"
+        }
+      },
+      "ADouble": {
+        "type": "number",
+        "format": "double"
+      },
+      "AnArrayOfFile": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "binary"
+        }
+      },
+      "AnOtherArrayOfFile": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AFile"
+        }
+      },
+      "AFile": {
+        "type": "string",
+        "format": "binary"
+      },
+      "AnArrayOfByteStream": {
+        "type": "array",
+        "items": {
+          "type": "string",
+          "format": "byte"
+        }
+      },
+      "AnOtherArrayOfByteStream": {
+        "type": "array",
+        "items": {
+          "$ref": "#/components/schemas/AByteStream"
+        }
+      },
+      "AByteStream": {
+        "type": "string",
+        "format": "byte"
       }
     }
   }
diff --git a/end_to_end_tests/regen_golden_record.py b/end_to_end_tests/regen_golden_record.py
index aaa6aa850..1d4dc943d 100644
--- a/end_to_end_tests/regen_golden_record.py
+++ b/end_to_end_tests/regen_golden_record.py
@@ -1,12 +1,16 @@
 """ Regenerate golden-record """
+import filecmp
+import os
 import shutil
+import tempfile
 from pathlib import Path
 
 from typer.testing import CliRunner
 
 from openapi_python_client.cli import app
 
-if __name__ == "__main__":
+
+def regen_golden_record():
     runner = CliRunner()
     openapi_path = Path(__file__).parent / "openapi.json"
 
@@ -24,3 +28,52 @@
     if result.exception:
         raise result.exception
     output_path.rename(gr_path)
+
+
+def regen_custom_template_golden_record():
+    runner = CliRunner()
+    openapi_path = Path(__file__).parent / "openapi.json"
+    tpl_dir = Path(__file__).parent / "test_custom_templates"
+
+    gr_path = Path(__file__).parent / "golden-record"
+    tpl_gr_path = Path(__file__).parent / "custom-templates-golden-record"
+
+    output_path = Path(tempfile.mkdtemp())
+    config_path = Path(__file__).parent / "config.yml"
+
+    shutil.rmtree(tpl_gr_path, ignore_errors=True)
+
+    os.chdir(str(output_path.absolute()))
+    result = runner.invoke(
+        app, ["generate", f"--config={config_path}", f"--path={openapi_path}", f"--custom-template-path={tpl_dir}"]
+    )
+
+    if result.stdout:
+        generated_output_path = output_path / "my-test-api-client"
+        for f in generated_output_path.glob("**/*"):  # nb: works for Windows and Unix
+            relative_to_generated = f.relative_to(generated_output_path)
+            gr_file = gr_path / relative_to_generated
+            if not gr_file.exists():
+                print(f"{gr_file} does not exist, ignoring")
+                continue
+
+            if not gr_file.is_file():
+                continue
+
+            if not filecmp.cmp(gr_file, f, shallow=False):
+                target_file = tpl_gr_path / relative_to_generated
+                target_dir = target_file.parent
+
+                target_dir.mkdir(parents=True, exist_ok=True)
+                shutil.copy(f"{f}", f"{target_file}")
+
+        shutil.rmtree(output_path, ignore_errors=True)
+
+    if result.exception:
+        shutil.rmtree(output_path, ignore_errors=True)
+        raise result.exception
+
+
+if __name__ == "__main__":
+    regen_golden_record()
+    regen_custom_template_golden_record()
diff --git a/end_to_end_tests/test_custom_templates/api_init.py.jinja b/end_to_end_tests/test_custom_templates/api_init.py.jinja
new file mode 100644
index 000000000..3655db659
--- /dev/null
+++ b/end_to_end_tests/test_custom_templates/api_init.py.jinja
@@ -0,0 +1,13 @@
+""" Contains methods for accessing the API """
+
+from typing import Type
+{% for tag, collection in endpoint_collections_by_tag %}
+from {{ package_name }}.api.{{ tag }} import {{ utils.pascal_case(tag) }}Endpoints
+{% endfor %}
+
+class {{ utils.pascal_case(package_name) }}Api:
+{% for tag, collection in endpoint_collections_by_tag %}
+    @classmethod
+    def {{ tag }}(cls) -> Type[{{ utils.pascal_case(tag) }}Endpoints]:
+        return {{ utils.pascal_case(tag) }}Endpoints
+{% endfor %}
diff --git a/end_to_end_tests/test_custom_templates/endpoint_init.py.jinja b/end_to_end_tests/test_custom_templates/endpoint_init.py.jinja
new file mode 100644
index 000000000..fbbd40e4f
--- /dev/null
+++ b/end_to_end_tests/test_custom_templates/endpoint_init.py.jinja
@@ -0,0 +1,24 @@
+""" Contains methods for accessing the API Endpoints """
+
+import types
+{% for endpoint in endpoints %}
+from {{ package_name }}.api.{{ tag }} import {{ utils.snake_case(endpoint.name) }}
+{% endfor %}
+
+class {{ utils.pascal_case(tag) }}Endpoints:
+
+{% for endpoint in endpoints %}
+
+    @classmethod
+    def {{ utils.snake_case(endpoint.name) }}(cls) -> types.ModuleType:
+        {% if endpoint.description %}
+        """
+            {{ endpoint.description }}
+        """
+        {% elif endpoint.summary %}
+        """
+            {{ endpoint.summary  }}
+        """
+        {% endif %}
+        return {{ utils.snake_case(endpoint.name) }}
+{% endfor %}
diff --git a/end_to_end_tests/test_end_to_end.py b/end_to_end_tests/test_end_to_end.py
index fa4d21598..ad552092a 100644
--- a/end_to_end_tests/test_end_to_end.py
+++ b/end_to_end_tests/test_end_to_end.py
@@ -12,7 +12,10 @@
 def _compare_directories(
     record: Path,
     test_subject: Path,
-    expected_differences: Optional[Dict[str, str]] = None,
+    expected_differences: Optional[
+        Dict[str, str]
+    ] = None,  # key: path relative to generated directory, value: expected generated content
+    depth=0,
 ):
     first_printable = record.relative_to(Path.cwd())
     second_printable = test_subject.relative_to(Path.cwd())
@@ -22,27 +25,41 @@ def _compare_directories(
         pytest.fail(f"{first_printable} or {second_printable} was missing: {missing_files}", pytrace=False)
 
     expected_differences = expected_differences or {}
-    _, mismatch, errors = cmpfiles(record, test_subject, dc.common_files, shallow=False)
-    mismatch = set(mismatch)
-
-    for file_name in mismatch | set(expected_differences.keys()):
-        if file_name not in expected_differences:
-            continue
-        if file_name not in mismatch:
-            pytest.fail(f"Expected {file_name} to be different but it was not", pytrace=False)
-        generated = (test_subject / file_name).read_text()
-        assert generated == expected_differences[file_name], f"Unexpected output in {file_name}"
-        del expected_differences[file_name]
-        mismatch.remove(file_name)
-
-    if mismatch:
+    _, mismatches, errors = cmpfiles(record, test_subject, dc.common_files, shallow=False)
+    mismatches = set(mismatches)
+
+    expected_path_mismatches = []
+    for file_name in mismatches:
+
+        mismatch_file_path = test_subject.joinpath(file_name)
+        for expected_differences_path in expected_differences.keys():
+
+            if mismatch_file_path.match(str(expected_differences_path)):
+
+                generated_content = (test_subject / file_name).read_text()
+                expected_content = expected_differences[expected_differences_path]
+                assert generated_content == expected_content, f"Unexpected output in {mismatch_file_path}"
+                expected_path_mismatches.append(expected_differences_path)
+
+    for path_mismatch in expected_path_mismatches:
+        matched_file_name = path_mismatch.name
+        mismatches.remove(matched_file_name)
+        del expected_differences[path_mismatch]
+
+    if mismatches:
         pytest.fail(
-            f"{first_printable} and {second_printable} had differing files: {mismatch}, and errors {errors}",
+            f"{first_printable} and {second_printable} had differing files: {mismatches}, and errors {errors}",
             pytrace=False,
         )
 
     for sub_path in dc.common_dirs:
-        _compare_directories(record / sub_path, test_subject / sub_path, expected_differences=expected_differences)
+        _compare_directories(
+            record / sub_path, test_subject / sub_path, expected_differences=expected_differences, depth=depth + 1
+        )
+
+    if depth == 0 and len(expected_differences.keys()) > 0:
+        failure = "\n".join([f"Expected {path} to be different but it was not" for path in expected_differences.keys()])
+        pytest.fail(failure, pytrace=False)
 
 
 def run_e2e_test(extra_args=None, expected_differences=None):
@@ -60,6 +77,7 @@ def run_e2e_test(extra_args=None, expected_differences=None):
 
     if result.exit_code != 0:
         raise result.exception
+
     _compare_directories(gr_path, output_path, expected_differences=expected_differences)
 
     import mypy.api
@@ -75,7 +93,21 @@ def test_end_to_end():
 
 
 def test_custom_templates():
+    expected_differences = {}  # key: path relative to generated directory, value: expected generated content
+    expected_difference_paths = [
+        Path("README.md"),
+        Path("my_test_api_client").joinpath("api", "__init__.py"),
+        Path("my_test_api_client").joinpath("api", "tests", "__init__.py"),
+        Path("my_test_api_client").joinpath("api", "default", "__init__.py"),
+        Path("my_test_api_client").joinpath("api", "parameters", "__init__.py"),
+    ]
+
+    golden_tpls_root_dir = Path(__file__).parent.joinpath("custom-templates-golden-record")
+    for expected_difference_path in expected_difference_paths:
+        path = Path("my-test-api-client").joinpath(expected_difference_path)
+        expected_differences[path] = (golden_tpls_root_dir / expected_difference_path).read_text()
+
     run_e2e_test(
-        extra_args=["--custom-template-path=end_to_end_tests/test_custom_templates"],
-        expected_differences={"README.md": "my-test-api-client"},
+        extra_args=["--custom-template-path=end_to_end_tests/test_custom_templates/"],
+        expected_differences=expected_differences,
     )
diff --git a/openapi_python_client/__init__.py b/openapi_python_client/__init__.py
index 2a7cf574b..43f813483 100644
--- a/openapi_python_client/__init__.py
+++ b/openapi_python_client/__init__.py
@@ -67,6 +67,7 @@ def __init__(
         else:
             loader = package_loader
         self.env: Environment = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
+        self.env.globals.update(utils=utils)
 
         self.project_name: str = config.project_name_override or f"{utils.kebab_case(openapi.title).lower()}-client"
         self.project_dir: Path = Path.cwd()
@@ -239,15 +240,30 @@ def _build_api(self) -> None:
         client_path.write_text(client_template.render(), encoding=self.file_encoding)
 
         # Generate endpoints
+        endpoint_collections_by_tag = self.openapi.endpoint_collections_by_tag.items()
         api_dir = self.package_dir / "api"
         api_dir.mkdir()
-        api_init = api_dir / "__init__.py"
-        api_init.write_text('""" Contains methods for accessing the API """', encoding=self.file_encoding)
+        api_init_path = api_dir / "__init__.py"
+        api_init_template = self.env.get_template("api_init.py.jinja")
+        api_init_path.write_text(
+            api_init_template.render(
+                package_name=self.package_name,
+                endpoint_collections_by_tag=endpoint_collections_by_tag,
+            ),
+            encoding=self.file_encoding,
+        )
 
         endpoint_template = self.env.get_template("endpoint_module.py.jinja")
-        for tag, collection in self.openapi.endpoint_collections_by_tag.items():
+        for tag, collection in endpoint_collections_by_tag:
             tag_dir = api_dir / tag
             tag_dir.mkdir()
+
+            endpoint_init_path = tag_dir / "__init__.py"
+            endpoint_init_template = self.env.get_template("endpoint_init.py.jinja")
+            endpoint_init_path.write_text(
+                endpoint_init_template.render(package_name=self.package_name, tag=tag, endpoints=collection.endpoints),
+                encoding=self.file_encoding,
+            )
             (tag_dir / "__init__.py").touch()
 
             for endpoint in collection.endpoints:
diff --git a/openapi_python_client/__main__.py b/openapi_python_client/__main__.py
index 4cafccbaf..f8cd76f35 100644
--- a/openapi_python_client/__main__.py
+++ b/openapi_python_client/__main__.py
@@ -1,3 +1,3 @@
-from .cli import cli
+from .cli import app
 
-cli()
+app()
diff --git a/openapi_python_client/parser/errors.py b/openapi_python_client/parser/errors.py
index 562e54428..6855ef6bd 100644
--- a/openapi_python_client/parser/errors.py
+++ b/openapi_python_client/parser/errors.py
@@ -1,6 +1,6 @@
 from dataclasses import dataclass
 from enum import Enum
-from typing import Optional
+from typing import Any, Optional
 
 __all__ = ["ErrorLevel", "GeneratorError", "ParseError", "PropertyError", "ValidationError"]
 
@@ -39,5 +39,12 @@ class PropertyError(ParseError):
     header = "Problem creating a Property: "
 
 
+@dataclass
+class RecursiveReferenceInterupt(PropertyError):
+    """Error raised when a property have an recursive reference to itself"""
+
+    schemas: Optional[Any] = None  # TODO: shall not use Any here, shall be Schemas, to fix later
+
+
 class ValidationError(Exception):
     pass
diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py
index ca8793d09..f0b9774ce 100644
--- a/openapi_python_client/parser/openapi.py
+++ b/openapi_python_client/parser/openapi.py
@@ -151,7 +151,14 @@ def _add_body(
             body=data.requestBody, schemas=schemas, parent_name=endpoint.name, config=config
         )
         if isinstance(json_body, ParseError):
-            return ParseError(detail=f"cannot parse body of endpoint {endpoint.name}", data=json_body.data), schemas
+            return (
+                ParseError(
+                    header=f"Cannot parse body of endpoint {endpoint.name}",
+                    detail=json_body.detail,
+                    data=json_body.data,
+                ),
+                schemas,
+            )
 
         endpoint.multipart_body_class = Endpoint.parse_multipart_body(body=data.requestBody, config=config)
 
diff --git a/openapi_python_client/parser/properties/.property.py.swp b/openapi_python_client/parser/properties/.property.py.swp
new file mode 100644
index 000000000..b8aa28d43
Binary files /dev/null and b/openapi_python_client/parser/properties/.property.py.swp differ
diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py
index 908745a6a..539559d17 100644
--- a/openapi_python_client/parser/properties/__init__.py
+++ b/openapi_python_client/parser/properties/__init__.py
@@ -17,12 +17,12 @@
 from ... import Config
 from ... import schema as oai
 from ... import utils
-from ..errors import ParseError, PropertyError, ValidationError
+from ..errors import ParseError, PropertyError, RecursiveReferenceInterupt, ValidationError
 from .converter import convert, convert_chain
 from .enum_property import EnumProperty
 from .model_property import ModelProperty, build_model_property
 from .property import Property
-from .schemas import Class, Schemas, parse_reference_path, update_schemas_with_data
+from .schemas import Class, Schemas, _Holder, _ReferencePath, parse_reference_path, update_schemas_with
 
 
 @attr.s(auto_attribs=True, frozen=True)
@@ -34,6 +34,59 @@ class NoneProperty(Property):
     template: ClassVar[Optional[str]] = "none_property.py.jinja"
 
 
+@attr.s(auto_attribs=True, frozen=True)
+class LazySelfReferenceProperty(Property):
+    """A property used to resolve recursive reference.
+    It proxyfy the required method call to its binded Property owner
+    """
+
+    owner: _Holder[Union[ModelProperty, EnumProperty, RecursiveReferenceInterupt]]
+    _resolved: bool = False
+
+    def get_base_type_string(self) -> str:
+        self._ensure_resolved()
+
+        prop = self.owner.data
+        assert isinstance(prop, Property)
+        return prop.get_base_type_string()
+
+    def get_base_json_type_string(self) -> str:
+        self._ensure_resolved()
+
+        prop = self.owner.data
+        assert isinstance(prop, Property)
+        return prop.get_base_json_type_string()
+
+    def get_type_string(self, no_optional: bool = False, json: bool = False) -> str:
+        self._ensure_resolved()
+
+        prop = self.owner.data
+        assert isinstance(prop, Property)
+        return prop.get_type_string(no_optional, json)
+
+    def get_instance_type_string(self) -> str:
+        self._ensure_resolved()
+        return super().get_instance_type_string()
+
+    def to_string(self) -> str:
+        self._ensure_resolved()
+
+        if not self.required:
+            return f"{self.python_name}: Union[Unset, {self.get_type_string()}] = UNSET"
+        else:
+            return f"{self.python_name}: {self.get_type_string()}"
+
+    def _ensure_resolved(self) -> None:
+        if self._resolved:
+            return
+
+        if not isinstance(self.owner.data, Property):
+            raise RuntimeError(f"LazySelfReferenceProperty {self.name} owner shall have been resolved.")
+        else:
+            object.__setattr__(self, "_resolved", True)
+            object.__setattr__(self, "nullable", self.owner.data.nullable)
+
+
 @attr.s(auto_attribs=True, frozen=True)
 class StringProperty(Property):
     """A property of type str"""
@@ -352,6 +405,7 @@ def build_union_property(
     *, data: oai.Schema, name: str, required: bool, schemas: Schemas, parent_name: str, config: Config
 ) -> Tuple[Union[UnionProperty, PropertyError], Schemas]:
     sub_properties: List[Property] = []
+
     for i, sub_prop_data in enumerate(chain(data.anyOf, data.oneOf)):
         sub_prop, schemas = property_from_data(
             name=f"{name}_type{i}",
@@ -410,11 +464,18 @@ def _property_from_ref(
     ref_path = parse_reference_path(data.ref)
     if isinstance(ref_path, ParseError):
         return PropertyError(data=data, detail=ref_path.detail), schemas
+
     existing = schemas.classes_by_reference.get(ref_path)
-    if not existing:
+    if not existing or not existing.data:
         return PropertyError(data=data, detail="Could not find reference in parsed models or enums"), schemas
 
-    prop = attr.evolve(existing, required=required, name=name)
+    if isinstance(existing.data, RecursiveReferenceInterupt):
+        return (
+            LazySelfReferenceProperty(required=required, name=name, nullable=False, default=None, owner=existing),
+            schemas,
+        )
+
+    prop = attr.evolve(existing.data, required=required, name=name)
     if parent:
         prop = attr.evolve(prop, nullable=parent.nullable)
         if isinstance(prop, EnumProperty):
@@ -439,8 +500,8 @@ def _property_from_data(
     if isinstance(data, oai.Reference):
         return _property_from_ref(name=name, required=required, parent=None, data=data, schemas=schemas)
 
+    sub_data: List[Union[oai.Schema, oai.Reference]] = data.allOf + data.anyOf + data.oneOf
     # A union of a single reference should just be passed through to that reference (don't create copy class)
-    sub_data = (data.allOf or []) + data.anyOf + data.oneOf
     if len(sub_data) == 1 and isinstance(sub_data[0], oai.Reference):
         return _property_from_ref(name=name, required=required, parent=data, data=sub_data[0], schemas=schemas)
 
@@ -550,29 +611,47 @@ def build_schemas(
     to_process: Iterable[Tuple[str, Union[oai.Reference, oai.Schema]]] = components.items()
     still_making_progress = True
     errors: List[PropertyError] = []
+    recursive_references_waiting_reprocess: Dict[str, Union[oai.Reference, oai.Schema]] = dict()
+    visited: Set[_ReferencePath] = set()
+    depth = 0
 
     # References could have forward References so keep going as long as we are making progress
     while still_making_progress:
         still_making_progress = False
         errors = []
         next_round = []
+
         # Only accumulate errors from the last round, since we might fix some along the way
         for name, data in to_process:
-            if isinstance(data, oai.Reference):
-                schemas.errors.append(PropertyError(data=data, detail="Reference schemas are not supported."))
-                continue
             ref_path = parse_reference_path(f"#/components/schemas/{name}")
             if isinstance(ref_path, ParseError):
                 schemas.errors.append(PropertyError(detail=ref_path.detail, data=data))
                 continue
-            schemas_or_err = update_schemas_with_data(ref_path=ref_path, data=data, schemas=schemas, config=config)
+
+            schemas_or_err = update_schemas_with(
+                ref_path=ref_path, data=data, schemas=schemas, visited=visited, config=config
+            )
             if isinstance(schemas_or_err, PropertyError):
-                next_round.append((name, data))
-                errors.append(schemas_or_err)
-                continue
+                ### TODO: Recursive reference detection does not properly works, disabled for now
+                #  visited.add(ref_path)
+
+                if isinstance(schemas_or_err, RecursiveReferenceInterupt):
+                    up_schemas = schemas_or_err.schemas
+                    assert isinstance(up_schemas, Schemas)  # TODO fix typedef in RecursiveReferenceInterupt
+                    schemas_or_err = up_schemas
+                    recursive_references_waiting_reprocess[name] = data
+                else:
+                    next_round.append((name, data))
+                    errors.append(schemas_or_err)
+                    continue
+
             schemas = schemas_or_err
             still_making_progress = True
+        depth += 1
         to_process = next_round
 
+    if len(recursive_references_waiting_reprocess.keys()):
+        schemas = build_schemas(components=recursive_references_waiting_reprocess, schemas=schemas, config=config)
+
     schemas.errors.extend(errors)
     return schemas
diff --git a/openapi_python_client/parser/properties/converter.py b/openapi_python_client/parser/properties/converter.py
index b493755fc..d8556e118 100644
--- a/openapi_python_client/parser/properties/converter.py
+++ b/openapi_python_client/parser/properties/converter.py
@@ -31,7 +31,7 @@ def convert(type_string: str, value: Any) -> Optional[Any]:
         raise ValidationError()
     try:
         return _CONVERTERS[type_string](value)
-    except (KeyError, ValueError) as e:
+    except (KeyError, ValueError, AttributeError) as e:
         raise ValidationError from e
 
 
@@ -58,8 +58,10 @@ def convert_chain(type_strings: Iterable[str], value: Any) -> Optional[Any]:
     raise ValidationError()
 
 
-def _convert_string(value: str) -> Optional[str]:
-    return f"{utils.remove_string_escapes(value)!r}"
+def _convert_string(value: Any) -> Optional[str]:
+    if isinstance(value, str):
+        value = utils.remove_string_escapes(value)
+    return repr(value)
 
 
 def _convert_datetime(value: str) -> Optional[str]:
diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py
index a40460886..70db0c5ed 100644
--- a/openapi_python_client/parser/properties/model_property.py
+++ b/openapi_python_client/parser/properties/model_property.py
@@ -11,6 +11,11 @@
 from .schemas import Class, Schemas, parse_reference_path
 
 
+@attr.s(auto_attribs=True, frozen=True)
+class RecusiveProperty(Property):
+    pass
+
+
 @attr.s(auto_attribs=True, frozen=True)
 class ModelProperty(Property):
     """A property which refers to another Schema"""
@@ -88,14 +93,17 @@ def _check_existing(prop: Property) -> Union[Property, PropertyError]:
         return prop_or_error
 
     unprocessed_props = data.properties or {}
-    for sub_prop in data.allOf or []:
+    for sub_prop in data.allOf:
         if isinstance(sub_prop, oai.Reference):
             ref_path = parse_reference_path(sub_prop.ref)
             if isinstance(ref_path, ParseError):
                 return PropertyError(detail=ref_path.detail, data=sub_prop)
-            sub_model = schemas.classes_by_reference.get(ref_path)
-            if sub_model is None:
+
+            sub_model_ref = schemas.classes_by_reference.get(ref_path)
+            if sub_model_ref is None or not isinstance(sub_model_ref.data, Property):
                 return PropertyError(f"Reference {sub_prop.ref} not found")
+
+            sub_model = sub_model_ref.data
             if not isinstance(sub_model, ModelProperty):
                 return PropertyError("Cannot take allOf a non-object")
             for prop in chain(sub_model.required_properties, sub_model.optional_properties):
diff --git a/openapi_python_client/parser/properties/schemas.py b/openapi_python_client/parser/properties/schemas.py
index a81879dfc..6e369e6ab 100644
--- a/openapi_python_client/parser/properties/schemas.py
+++ b/openapi_python_client/parser/properties/schemas.py
@@ -1,6 +1,6 @@
-__all__ = ["Class", "Schemas", "parse_reference_path", "update_schemas_with_data"]
+__all__ = ["Class", "Schemas", "parse_reference_path", "update_schemas_with", "_ReferencePath"]
 
-from typing import TYPE_CHECKING, Dict, List, NewType, Union, cast
+from typing import TYPE_CHECKING, Dict, Generic, List, NewType, Optional, Set, TypeVar, Union, cast
 from urllib.parse import urlparse
 
 import attr
@@ -8,16 +8,14 @@
 from ... import Config
 from ... import schema as oai
 from ... import utils
-from ..errors import ParseError, PropertyError
+from ..errors import ParseError, PropertyError, RecursiveReferenceInterupt
 
 if TYPE_CHECKING:  # pragma: no cover
-    from .enum_property import EnumProperty
-    from .model_property import ModelProperty
+    from .property import Property
 else:
-    EnumProperty = "EnumProperty"
-    ModelProperty = "ModelProperty"
-
+    Property = "Property"
 
+T = TypeVar("T")
 _ReferencePath = NewType("_ReferencePath", str)
 _ClassName = NewType("_ClassName", str)
 
@@ -29,6 +27,11 @@ def parse_reference_path(ref_path_raw: str) -> Union[_ReferencePath, ParseError]
     return cast(_ReferencePath, parsed.fragment)
 
 
+@attr.s(auto_attribs=True)
+class _Holder(Generic[T]):
+    data: Optional[T]
+
+
 @attr.s(auto_attribs=True, frozen=True)
 class Class:
     """Represents Python class which will be generated from an OpenAPI schema"""
@@ -58,26 +61,67 @@ def from_string(*, string: str, config: Config) -> "Class":
 class Schemas:
     """Structure for containing all defined, shareable, and reusable schemas (attr classes and Enums)"""
 
-    classes_by_reference: Dict[_ReferencePath, Union[EnumProperty, ModelProperty]] = attr.ib(factory=dict)
-    classes_by_name: Dict[_ClassName, Union[EnumProperty, ModelProperty]] = attr.ib(factory=dict)
+    classes_by_reference: Dict[
+        _ReferencePath, _Holder[Union[Property, RecursiveReferenceInterupt]]
+    ] = attr.ib(factory=dict)
+    classes_by_name: Dict[
+        _ClassName, _Holder[Union[Property, RecursiveReferenceInterupt]]
+    ] = attr.ib(factory=dict)
     errors: List[ParseError] = attr.ib(factory=list)
 
 
-def update_schemas_with_data(
-    *, ref_path: _ReferencePath, data: oai.Schema, schemas: Schemas, config: Config
+def update_schemas_with(
+    *,
+    ref_path: _ReferencePath,
+    data: Union[oai.Reference, oai.Schema],
+    schemas: Schemas,
+    visited: Set[_ReferencePath],
+    config: Config,
 ) -> Union[Schemas, PropertyError]:
-    from . import build_enum_property, build_model_property
-
-    prop: Union[PropertyError, ModelProperty, EnumProperty]
-    if data.enum is not None:
-        prop, schemas = build_enum_property(
-            data=data, name=ref_path, required=True, schemas=schemas, enum=data.enum, parent_name=None, config=config
+    if isinstance(data, oai.Reference):
+        return _update_schemas_with_reference(
+            ref_path=ref_path, data=data, schemas=schemas, visited=visited, config=config
         )
     else:
-        prop, schemas = build_model_property(
-            data=data, name=ref_path, schemas=schemas, required=True, parent_name=None, config=config
-        )
+        return _update_schemas_with_data(ref_path=ref_path, data=data, schemas=schemas, visited=visited, config=config)
+
+
+def _update_schemas_with_reference(
+    *, ref_path: _ReferencePath, data: oai.Reference, schemas: Schemas, visited: Set[_ReferencePath], config: Config
+) -> Union[Schemas, PropertyError]:
+    reference_pointer = parse_reference_path(data.ref)
+    if isinstance(reference_pointer, ParseError):
+        return PropertyError(detail=reference_pointer.detail, data=data)
+
+    resolved_reference = schemas.classes_by_reference.get(reference_pointer)
+    if resolved_reference:
+        return attr.evolve(schemas, classes_by_reference={ref_path: resolved_reference, **schemas.classes_by_reference})
+    else:
+        return PropertyError(f"Reference {ref_path} could not be resolved", data=data)
+
+
+def _update_schemas_with_data(
+    *, ref_path: _ReferencePath, data: oai.Schema, schemas: Schemas, visited: Set[_ReferencePath], config: Config
+) -> Union[Schemas, PropertyError]:
+    from . import property_from_data
+
+    prop: Union[PropertyError, Property]
+    prop, schemas = property_from_data(
+        data=data, name=ref_path, schemas=schemas, required=True, parent_name="", config=config
+    )
+
+    holder = schemas.classes_by_reference.get(ref_path)
     if isinstance(prop, PropertyError):
+        if ref_path in visited and not holder:
+            holder = _Holder(data=RecursiveReferenceInterupt())
+            schemas = attr.evolve(schemas, classes_by_reference={ref_path: holder, **schemas.classes_by_reference})
+            return RecursiveReferenceInterupt(schemas=schemas)
         return prop
-    schemas = attr.evolve(schemas, classes_by_reference={ref_path: prop, **schemas.classes_by_reference})
+
+    if holder:
+        holder.data = prop
+    else:
+        schemas = attr.evolve(
+            schemas, classes_by_reference={ref_path: _Holder(data=prop), **schemas.classes_by_reference}
+        )
     return schemas
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/README.md b/openapi_python_client/schema/openapi_schema_pydantic/README.md
index 0e4d40146..f58b36909 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/README.md
+++ b/openapi_python_client/schema/openapi_schema_pydantic/README.md
@@ -1,5 +1,8 @@
 Everything in this directory (including the rest of this file after this paragraph) is a vendored copy of [openapi-schem-pydantic](https://github.com/kuimono/openapi-schema-pydantic) and is licensed under the LICENSE file in this directory.
 
+Included vendored version is the [following](https://github.com/kuimono/openapi-schema-pydantic/commit/0836b429086917feeb973de3367a7ac4c2b3a665)
+Small patches has been applied to it.
+
 ## Alias
 
 Due to the reserved words in python and pydantic,
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/__init__.py b/openapi_python_client/schema/openapi_schema_pydantic/__init__.py
index 9edb7d3d9..6b02446a8 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/__init__.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/__init__.py
@@ -36,8 +36,11 @@
     "ServerVariable",
     "Tag",
     "XML",
+    "Callback",
 ]
 
+
+from .callback import Callback
 from .components import Components
 from .contact import Contact
 from .discriminator import Discriminator
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/callback.py b/openapi_python_client/schema/openapi_schema_pydantic/callback.py
new file mode 100644
index 000000000..9d95ffe89
--- /dev/null
+++ b/openapi_python_client/schema/openapi_schema_pydantic/callback.py
@@ -0,0 +1,22 @@
+from typing import TYPE_CHECKING, Dict
+
+if TYPE_CHECKING:
+    from .path_item import PathItem
+
+Callback = Dict[str, "PathItem"]
+"""
+A map of possible out-of band callbacks related to the parent operation.
+Each value in the map is a [Path Item Object](#pathItemObject)
+that describes a set of requests that may be initiated by the API provider and the expected responses.
+The key value used to identify the path item object is an expression, evaluated at runtime,
+that identifies a URL to use for the callback operation.
+"""
+
+"""Patterned Fields"""
+
+# {expression}: 'PathItem' = ...
+"""
+A Path Item Object used to define a callback request and expected responses.
+
+A [complete example](../examples/v3.0/callback-example.yaml) is available.
+"""
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/components.py b/openapi_python_client/schema/openapi_schema_pydantic/components.py
index 8ad888906..a9a2f0339 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/components.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/components.py
@@ -1,7 +1,8 @@
 from typing import Dict, Optional, Union
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
+from .callback import Callback
 from .example import Example
 from .header import Header
 from .link import Link
@@ -44,7 +45,11 @@ class Components(BaseModel):
     links: Optional[Dict[str, Union[Link, Reference]]] = None
     """An object to hold reusable [Link Objects](#linkObject)."""
 
+    callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None
+    """An object to hold reusable [Callback Objects](#callbackObject)."""
+
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/contact.py b/openapi_python_client/schema/openapi_schema_pydantic/contact.py
index c41aa8406..cbe2fb8eb 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/contact.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/contact.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import AnyUrl, BaseModel
+from pydantic import AnyUrl, BaseModel, Extra
 
 
 class Contact(BaseModel):
@@ -26,6 +26,7 @@ class Contact(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {"name": "API Support", "url": "http://www.example.com/support", "email": "support@example.com"}
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py
index 1dfd06a5b..c2deb5a17 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/discriminator.py
@@ -1,6 +1,6 @@
 from typing import Dict, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 
 class Discriminator(BaseModel):
@@ -25,6 +25,7 @@ class Discriminator(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/encoding.py b/openapi_python_client/schema/openapi_schema_pydantic/encoding.py
index 9bf2ea177..a9c08a67e 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/encoding.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/encoding.py
@@ -1,9 +1,12 @@
-from typing import Dict, Optional
+from typing import TYPE_CHECKING, Dict, Optional, Union
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .reference import Reference
 
+if TYPE_CHECKING:
+    from .header import Header
+
 
 class Encoding(BaseModel):
     """A single encoding definition applied to a single schema property."""
@@ -22,7 +25,7 @@ class Encoding(BaseModel):
     or a comma-separated list of the two types.
     """
 
-    headers: Optional[Dict[str, Reference]] = None
+    headers: Optional[Dict[str, Union["Header", Reference]]] = None
     """
     A map allowing additional information to be provided as headers, for example `Content-Disposition`.
 
@@ -60,6 +63,7 @@ class Encoding(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/example.py b/openapi_python_client/schema/openapi_schema_pydantic/example.py
index 6da3710b1..4ac234b37 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/example.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/example.py
@@ -1,6 +1,6 @@
 from typing import Any, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 
 class Example(BaseModel):
@@ -33,6 +33,7 @@ class Example(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {"summary": "A foo example", "value": {"foo": "bar"}},
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py b/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py
index 92676a2ae..467c98150 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import AnyUrl, BaseModel
+from pydantic import AnyUrl, BaseModel, Extra
 
 
 class ExternalDocumentation(BaseModel):
@@ -19,4 +19,5 @@ class ExternalDocumentation(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {"examples": [{"description": "Find more info here", "url": "https://example.com"}]}
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/header.py b/openapi_python_client/schema/openapi_schema_pydantic/header.py
index 5e59e8a66..8d6ee2ff1 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/header.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/header.py
@@ -1,4 +1,4 @@
-from pydantic import Field
+from pydantic import Extra, Field
 
 from ..parameter_location import ParameterLocation
 from .parameter import Parameter
@@ -18,6 +18,7 @@ class Header(Parameter):
     param_in = Field(default=ParameterLocation.HEADER, const=True, alias="in")
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/info.py b/openapi_python_client/schema/openapi_schema_pydantic/info.py
index 36caba733..fcff5b742 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/info.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/info.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import AnyUrl, BaseModel
+from pydantic import AnyUrl, BaseModel, Extra
 
 from .contact import Contact
 from .license import License
@@ -47,6 +47,7 @@ class Info(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/license.py b/openapi_python_client/schema/openapi_schema_pydantic/license.py
index 567a5d117..7e8b7f3d9 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/license.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/license.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import AnyUrl, BaseModel
+from pydantic import AnyUrl, BaseModel, Extra
 
 
 class License(BaseModel):
@@ -20,4 +20,5 @@ class License(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {"examples": [{"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html"}]}
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/link.py b/openapi_python_client/schema/openapi_schema_pydantic/link.py
index 97bbc8aeb..de1403a8c 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/link.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/link.py
@@ -1,6 +1,6 @@
 from typing import Any, Dict, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .server import Server
 
@@ -63,6 +63,7 @@ class Link(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {"operationId": "getUserAddressByUUID", "parameters": {"userUuid": "$response.body#/uuid"}},
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/media_type.py b/openapi_python_client/schema/openapi_schema_pydantic/media_type.py
index 7c00d3bc1..387f33b92 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/media_type.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/media_type.py
@@ -1,6 +1,6 @@
 from typing import Any, Dict, Optional, Union
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Extra, Field
 
 from .encoding import Encoding
 from .example import Example
@@ -49,6 +49,7 @@ class MediaType(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py
index cdf474bee..7b3c93bfe 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py
@@ -1,6 +1,6 @@
 from typing import Dict, Optional
 
-from pydantic import AnyUrl, BaseModel
+from pydantic import AnyUrl, BaseModel, Extra
 
 
 class OAuthFlow(BaseModel):
@@ -15,7 +15,7 @@ class OAuthFlow(BaseModel):
     This MUST be in the form of a URL.
     """
 
-    tokenUrl: Optional[str] = None
+    tokenUrl: Optional[AnyUrl] = None
     """
     **REQUIRED** for `oauth2 ("password", "clientCredentials", "authorizationCode")`.
     The token URL to be used for this flow.
@@ -35,6 +35,7 @@ class OAuthFlow(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py
index fcb9ba348..e23b3d5dd 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/oauth_flows.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .oauth_flow import OAuthFlow
 
@@ -33,3 +33,6 @@ class OAuthFlows(BaseModel):
 
     Previously called `accessCode` in OpenAPI 2.0.
     """
+
+    class Config:
+        extra = Extra.allow
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py
index dd480a8f5..cbfaa2038 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/open_api.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/open_api.py
@@ -1,6 +1,6 @@
 from typing import List, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .components import Components
 from .external_documentation import ExternalDocumentation
@@ -58,3 +58,6 @@ class OpenAPI(BaseModel):
     """
     Additional external documentation.
     """
+
+    class Config:
+        extra = Extra.allow
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/operation.py b/openapi_python_client/schema/openapi_schema_pydantic/operation.py
index 860c3c24a..98b2bd2ad 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/operation.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/operation.py
@@ -1,7 +1,8 @@
-from typing import List, Optional, Union
+from typing import Dict, List, Optional, Union
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
+from .callback import Callback
 from .external_documentation import ExternalDocumentation
 from .parameter import Parameter
 from .reference import Reference
@@ -70,6 +71,14 @@ class Operation(BaseModel):
     **REQUIRED**. The list of possible responses as they are returned from executing this operation.
     """
 
+    callbacks: Optional[Dict[str, Callback]] = None
+    """
+    A map of possible out-of band callbacks related to the parent operation.
+    The key is a unique identifier for the Callback Object.
+    Each value in the map is a [Callback Object](#callbackObject)
+    that describes a request that may be initiated by the API provider and the expected responses.
+    """
+
     deprecated: bool = False
     """
     Declares this operation to be deprecated.
@@ -95,6 +104,7 @@ class Operation(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/parameter.py b/openapi_python_client/schema/openapi_schema_pydantic/parameter.py
index 52f1b6885..56070a720 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/parameter.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/parameter.py
@@ -1,6 +1,6 @@
 from typing import Any, Dict, Optional, Union
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Extra, Field
 
 from ..parameter_location import ParameterLocation
 from .example import Example
@@ -26,7 +26,7 @@ class Parameter(BaseModel):
     - If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a template expression occurring
       within the [path](#pathsPath) field in the [Paths Object](#pathsObject).
       See [Path Templating](#pathTemplating) for further information.
-    - If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`, `"Content-Type"` or `"Authorization"`.
+    - If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`, `"Content-Type"` or `"Authorization"`,
       the parameter definition SHALL be ignored.
     - For all other cases, the `name` corresponds to the parameter name used by the [`in`](#parameterIn) property.
     """
@@ -142,6 +142,7 @@ class Parameter(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/path_item.py b/openapi_python_client/schema/openapi_schema_pydantic/path_item.py
index 911c1e805..cdbd6b564 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/path_item.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/path_item.py
@@ -1,6 +1,6 @@
 from typing import List, Optional, Union
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Extra, Field
 
 from .operation import Operation
 from .parameter import Parameter
@@ -92,6 +92,7 @@ class PathItem(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/reference.py b/openapi_python_client/schema/openapi_schema_pydantic/reference.py
index 7803b3a54..851b28301 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/reference.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/reference.py
@@ -1,4 +1,4 @@
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Extra, Field
 
 
 class Reference(BaseModel):
@@ -16,6 +16,7 @@ class Reference(BaseModel):
     """**REQUIRED**. The reference string."""
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [{"$ref": "#/components/schemas/Pet"}, {"$ref": "Pet.json"}, {"$ref": "definitions.json#/Pet"}]
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/request_body.py b/openapi_python_client/schema/openapi_schema_pydantic/request_body.py
index 626b795d7..3f7a602d9 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/request_body.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/request_body.py
@@ -1,6 +1,6 @@
 from typing import Dict, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .media_type import MediaType
 
@@ -31,6 +31,7 @@ class RequestBody(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/response.py b/openapi_python_client/schema/openapi_schema_pydantic/response.py
index 8c8e539ec..24899815e 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/response.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/response.py
@@ -1,6 +1,6 @@
 from typing import Dict, Optional, Union
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .header import Header
 from .link import Link
@@ -44,6 +44,7 @@ class Response(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/schema.py b/openapi_python_client/schema/openapi_schema_pydantic/schema.py
index 5941d79f5..87492eadc 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/schema.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/schema.py
@@ -1,6 +1,6 @@
 from typing import Any, Dict, List, Optional, Union
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Extra, Field
 
 from .discriminator import Discriminator
 from .external_documentation import ExternalDocumentation
@@ -224,7 +224,7 @@ class Schema(BaseModel):
     types defined by keyword.  Recall: "number" includes "integer".
     """
 
-    allOf: Optional[List[Union[Reference, "Schema"]]] = None
+    allOf: List[Union[Reference, "Schema"]] = Field(default_factory=list)
     """
     **From OpenAPI spec:
     Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.**
@@ -241,7 +241,7 @@ class Schema(BaseModel):
     value.
     """
 
-    oneOf: List[Union[Reference, "Schema"]] = []
+    oneOf: List[Union[Reference, "Schema"]] = Field(default_factory=list)
     """
     **From OpenAPI spec:
     Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.**
@@ -258,7 +258,7 @@ class Schema(BaseModel):
     keyword's value.
     """
 
-    anyOf: List[Union[Reference, "Schema"]] = []
+    anyOf: List[Union[Reference, "Schema"]] = Field(default_factory=list)
     """
     **From OpenAPI spec:
     Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.**
@@ -472,6 +472,7 @@ class Schema(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py b/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py
index aee65bc2b..7bd3b4e8e 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import AnyUrl, BaseModel, Field
+from pydantic import AnyUrl, BaseModel, Extra, Field
 
 from .oauth_flows import OAuthFlows
 
@@ -66,6 +66,7 @@ class SecurityScheme(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         allow_population_by_field_name = True
         schema_extra = {
             "examples": [
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/server.py b/openapi_python_client/schema/openapi_schema_pydantic/server.py
index 43c511f34..949fec8c8 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/server.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/server.py
@@ -1,6 +1,6 @@
 from typing import Dict, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .server_variable import ServerVariable
 
@@ -31,6 +31,7 @@ class Server(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {"url": "https://development.gigantic-server.com/v1", "description": "Development server"},
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py b/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py
index 224c79411..8aa4fac46 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/server_variable.py
@@ -1,6 +1,6 @@
 from typing import List, Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 
 class ServerVariable(BaseModel):
@@ -26,3 +26,6 @@ class ServerVariable(BaseModel):
     An optional description for the server variable.
     [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
     """
+
+    class Config:
+        extra = Extra.allow
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/tag.py b/openapi_python_client/schema/openapi_schema_pydantic/tag.py
index 531de02b4..1db40d325 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/tag.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/tag.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 from .external_documentation import ExternalDocumentation
 
@@ -28,4 +28,5 @@ class Tag(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {"examples": [{"name": "pet", "description": "Pets operations"}]}
diff --git a/openapi_python_client/schema/openapi_schema_pydantic/xml.py b/openapi_python_client/schema/openapi_schema_pydantic/xml.py
index 9ddaf13e3..cfc9c64cb 100644
--- a/openapi_python_client/schema/openapi_schema_pydantic/xml.py
+++ b/openapi_python_client/schema/openapi_schema_pydantic/xml.py
@@ -1,6 +1,6 @@
 from typing import Optional
 
-from pydantic import BaseModel
+from pydantic import BaseModel, Extra
 
 
 class XML(BaseModel):
@@ -48,6 +48,7 @@ class XML(BaseModel):
     """
 
     class Config:
+        extra = Extra.allow
         schema_extra = {
             "examples": [
                 {"namespace": "http://example.com/schema/sample", "prefix": "sample"},
diff --git a/openapi_python_client/templates/api_init.py.jinja b/openapi_python_client/templates/api_init.py.jinja
new file mode 100644
index 000000000..dc035f4ce
--- /dev/null
+++ b/openapi_python_client/templates/api_init.py.jinja
@@ -0,0 +1 @@
+""" Contains methods for accessing the API """
diff --git a/openapi_python_client/templates/endpoint_init.py.jinja b/openapi_python_client/templates/endpoint_init.py.jinja
new file mode 100644
index 000000000..e69de29bb
diff --git a/openapi_python_client/templates/endpoint_module.py.jinja b/openapi_python_client/templates/endpoint_module.py.jinja
index 06876141a..687705def 100644
--- a/openapi_python_client/templates/endpoint_module.py.jinja
+++ b/openapi_python_client/templates/endpoint_module.py.jinja
@@ -1,7 +1,6 @@
 from typing import Any, Dict, List, Optional, Union, cast
 
 import httpx
-from attr import asdict
 
 from ...client import AuthenticatedClient, Client
 from ...types import Response, UNSET{% if endpoint.multipart_body_class %}, File {% endif %}
@@ -52,7 +51,7 @@ def _get_kwargs(
         "cookies": cookies,
         "timeout": client.get_timeout(),
         {% if endpoint.form_body_class %}
-        "data": asdict(form_data),
+        "data": form_data.to_dict(),
         {% elif endpoint.multipart_body_class %}
         "files": files,
         "data": data,
diff --git a/openapi_python_client/templates/model.py.jinja b/openapi_python_client/templates/model.py.jinja
index 8541db32d..0cc98b105 100644
--- a/openapi_python_client/templates/model.py.jinja
+++ b/openapi_python_client/templates/model.py.jinja
@@ -1,4 +1,4 @@
-from typing import Any, Dict, Type, TypeVar
+from typing import Any, Dict, Type, TypeVar, Tuple, Optional, BinaryIO, TextIO
 
 {% if model.additional_properties %}
 from typing import List
diff --git a/openapi_python_client/templates/property_templates/property_macros.py.jinja b/openapi_python_client/templates/property_templates/property_macros.py.jinja
index 92669cdc2..d578d1d4f 100644
--- a/openapi_python_client/templates/property_templates/property_macros.py.jinja
+++ b/openapi_python_client/templates/property_templates/property_macros.py.jinja
@@ -1,16 +1,20 @@
 {% macro construct_template(construct_function, property, source, initial_value=None) %}
 {% if property.required and not property.nullable %}
 {{ property.python_name }} = {{ construct_function(property, source) }}
-{% else %}
-{% if initial_value != None %}
-{{ property.python_name }} = {{ initial_value }}
-{% elif property.nullable %}
-{{ property.python_name }} = None
-{% else %}
-{{ property.python_name }}: {{ property.get_type_string() }} = UNSET
-{% endif %}
+{% else %}{# Must be nullable OR non-required #}
 _{{ 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 %}:
+{{ property.python_name }}: {{ property.get_type_string() }}
+    {% if property.nullable %}
+if _{{ property.python_name }} is None:
+    {{ property.python_name }} = {% if initial_value != None %}{{ initial_value }}{% else %}None{% endif %}
+
+    {% endif %}
+    {% if not property.required %}
+{% if property.nullable %}elif{% else %}if{% endif %} isinstance(_{{ property.python_name }},  Unset):
+    {{ property.python_name }} = {% if initial_value != None %}{{ initial_value }}{% else %}UNSET{% endif %}
+
+    {% endif %}
+else:
     {{ property.python_name }} = {{ construct_function(property, "_" + property.python_name) }}
 {% endif %}
 {% endmacro %}
diff --git a/openapi_python_client/templates/property_templates/union_property.py.jinja b/openapi_python_client/templates/property_templates/union_property.py.jinja
index 684ae942d..87ea9820f 100644
--- a/openapi_python_client/templates/property_templates/union_property.py.jinja
+++ b/openapi_python_client/templates/property_templates/union_property.py.jinja
@@ -11,7 +11,6 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri
     {% for inner_property in property.inner_properties_with_template() %}
     {% if not loop.last or property.has_properties_without_templates %}
     try:
-        {{ inner_property.python_name }}: {{ inner_property.get_type_string() }}
     {% from "property_templates/" + inner_property.template import construct, check_type_for_construct %}
         if not {{ check_type_for_construct(inner_property, "data") }}:
             raise TypeError()
@@ -23,7 +22,6 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri
     {% from "property_templates/" + inner_property.template import construct, check_type_for_construct %}
     if not {{ check_type_for_construct(inner_property, "data") }}:
         raise TypeError()
-    {{ inner_property.python_name }}: {{ inner_property.get_type_string() }}
     {{ construct(inner_property, "data", initial_value="UNSET") | indent(4) }}
     return {{ inner_property.python_name }}
     {% endif %}
diff --git a/poetry.lock b/poetry.lock
index 7db8957b7..0dba5fd26 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -259,25 +259,25 @@ colors = ["colorama (>=0.4.3,<0.5.0)"]
 
 [[package]]
 name = "jinja2"
-version = "2.11.3"
+version = "3.0.0"
 description = "A very fast and expressive template engine."
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
 
 [package.dependencies]
-MarkupSafe = ">=0.23"
+MarkupSafe = ">=2.0.0rc2"
 
 [package.extras]
-i18n = ["Babel (>=0.8)"]
+i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "markupsafe"
-version = "1.1.1"
+version = "2.0.0"
 description = "Safely add untrusted strings to HTML/XML markup."
 category = "main"
 optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "mccabe"
@@ -521,7 +521,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
 
 [[package]]
 name = "rfc3986"
-version = "1.4.0"
+version = "1.5.0"
 description = "Validating URI References per RFC 3986"
 category = "main"
 optional = false
@@ -670,7 +670,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.6.2"
-content-hash = "b125cc4bc5456b335d0412e87dc21175738b4138a6eb9f4689b79d07171b8273"
+content-hash = "41d160b8f136bcba70b7be42ffc5b11f9011671f49c88f51e75a537aa15adec1"
 
 [metadata.files]
 appdirs = [
@@ -806,62 +806,44 @@ isort = [
     {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"},
 ]
 jinja2 = [
-    {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
-    {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
+    {file = "Jinja2-3.0.0-py3-none-any.whl", hash = "sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6"},
+    {file = "Jinja2-3.0.0.tar.gz", hash = "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"},
 ]
 markupsafe = [
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"},
-    {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-win32.whl", hash = "sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715"},
+    {file = "MarkupSafe-2.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-win32.whl", hash = "sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66"},
+    {file = "MarkupSafe-2.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-win32.whl", hash = "sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d"},
+    {file = "MarkupSafe-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-win32.whl", hash = "sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05"},
+    {file = "MarkupSafe-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2"},
+    {file = "MarkupSafe-2.0.0.tar.gz", hash = "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527"},
 ]
 mccabe = [
     {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
@@ -1048,8 +1030,8 @@ requests = [
     {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
 ]
 rfc3986 = [
-    {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
-    {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
+    {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+    {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
 ]
 safety = [
     {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"},
diff --git a/pyproject.toml b/pyproject.toml
index d39350c1a..37847579b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "openapi-python-client"
-version = "0.9.0"
+version = "0.9.1"
 description = "Generate modern Python clients from OpenAPI"
 repository = "https://github.com/triaxtec/openapi-python-client"
 license = "MIT"
@@ -20,11 +20,11 @@ include = ["CHANGELOG.md", "openapi_python_client/py.typed"]
 
 [tool.poetry.dependencies]
 python = "^3.6.2"
-jinja2 = "^2.11.1"
+jinja2 = "^3.0.0"
 typer = "^0.3"
 colorama = {version = "^0.4.3", markers = "sys_platform == 'win32'"}
 shellingham = "^1.3.2"
-black = "^21.4b0"
+black = "*"
 isort = "^5.0.5"
 pyyaml = "^5.3.1"
 importlib_metadata = {version = "^2.0.0", python = "<3.8"}
diff --git a/tests/test___main__.py b/tests/test___main__.py
index 4e2a51546..79ef06e0a 100644
--- a/tests/test___main__.py
+++ b/tests/test___main__.py
@@ -1,7 +1,7 @@
 def test_main(mocker):
-    cli = mocker.patch("openapi_python_client.cli.cli")
+    app = mocker.patch("openapi_python_client.cli.app")
 
     # noinspection PyUnresolvedReferences
     from openapi_python_client import __main__
 
-    cli.assert_called_once()
+    app.assert_called_once()
diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py
index c496b7dc2..a220d243c 100644
--- a/tests/test_parser/test_openapi.py
+++ b/tests/test_parser/test_openapi.py
@@ -234,7 +234,7 @@ def test_add_body_bad_data(self, mocker):
         from openapi_python_client.parser.openapi import Endpoint, Schemas
 
         mocker.patch.object(Endpoint, "parse_request_form_body")
-        parse_error = ParseError(data=mocker.MagicMock())
+        parse_error = ParseError(data=mocker.MagicMock(), detail=mocker.MagicMock())
         other_schemas = mocker.MagicMock()
         mocker.patch.object(Endpoint, "parse_request_json_body", return_value=(parse_error, other_schemas))
         endpoint = self.make_endpoint()
@@ -249,7 +249,11 @@ def test_add_body_bad_data(self, mocker):
         )
 
         assert result == (
-            ParseError(detail=f"cannot parse body of endpoint {endpoint.name}", data=parse_error.data),
+            ParseError(
+                header=f"Cannot parse body of endpoint {endpoint.name}",
+                detail=parse_error.detail,
+                data=parse_error.data,
+            ),
             other_schemas,
         )
 
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 0b44852df..23208c971 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
@@ -7,9 +7,13 @@
 if not isinstance(some_source, Unset):
     some_destination = some_source.isoformat() if some_source else None
 
-a_prop = None
 _a_prop = some_destination
-if _a_prop is not None and not isinstance(_a_prop,  Unset):
+a_prop: Union[Unset, None, datetime.date]
+if _a_prop is None:
+    a_prop = None
+elif isinstance(_a_prop,  Unset):
+    a_prop = UNSET
+else:
     a_prop = isoparse(_a_prop).date()
 
 
diff --git a/tests/test_templates/test_property_templates/test_date_property/required_nullable.py b/tests/test_templates/test_property_templates/test_date_property/required_nullable.py
index f974c3210..79dd66ba4 100644
--- a/tests/test_templates/test_property_templates/test_date_property/required_nullable.py
+++ b/tests/test_templates/test_property_templates/test_date_property/required_nullable.py
@@ -4,9 +4,11 @@
 from dateutil.parser import isoparse
 some_source = date(2020, 10, 12)
 some_destination = some_source.isoformat() if some_source else None 
-a_prop = None
 _a_prop = some_destination
-if _a_prop is not None:
+a_prop: Optional[datetime.date]
+if _a_prop is None:
+    a_prop = None
+else:
     a_prop = isoparse(_a_prop).date()