{% macro construct(property, source) %}
def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}:
    {% if "None" in property.get_type_strings_in_union(json=True) %}
    if data is None:
        return data
    {% endif %}
    {% if "Unset" in property.get_type_strings_in_union(json=True) %}
    if isinstance(data, Unset):
        return data
    {% endif %}
    {% set ns = namespace(contains_unmodified_properties = false) %}
    {% for inner_property in property.inner_properties %}
    {% import "property_templates/" + inner_property.template as inner_template %}
        {% if not inner_template.construct %}
            {% set ns.contains_unmodified_properties = true %}
            {% continue %}
        {% endif %}
    {% if inner_template.check_type_for_construct and (not loop.last or ns.contains_unmodified_properties) %}
    try:
        if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
            raise TypeError()
        {{ inner_template.construct(inner_property, "data") | indent(8) }}
        return {{ inner_property.python_name }}
    except: # noqa: E722
        pass
    {% else  %}{# Don't do try/except for the last one nor any properties with no type checking #}
    {% if inner_template.check_type_for_construct %}
    if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
        raise TypeError()
    {% endif %}
    {{ inner_template.construct(inner_property, "data") | indent(4) }}
    return {{ inner_property.python_name }}
    {% endif %}
    {% endfor %}
    {% if ns.contains_unmodified_properties %}
    return cast({{ property.get_type_string() }}, data)
    {% endif %}

{{ property.python_name }} = _parse_{{ property.python_name }}({{ source }})
{% endmacro %}

{% macro transform(property, source, destination, declare_type=True) %}
{% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required, has_if = false) %}
{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) }}{% endif %}

{% if not property.required %}
if isinstance({{ source }}, Unset):
    {{ destination }} = UNSET
    {% set ns.has_if = true %}
{% endif %}
{% for inner_property in property.inner_properties %}
    {% import "property_templates/" + inner_property.template as inner_template %}
    {% if not inner_template.transform %}
        {% set ns.contains_properties_without_transform = true %}
        {% continue %}
    {% else %}
        {% set ns.contains_modified_properties = true %}
    {% endif %}
    {% if not ns.has_if %}
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
        {% set ns.has_if = true %}
    {% elif not loop.last or ns.contains_properties_without_transform %}
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
    {% else %}
else:
    {% endif %}
    {{ inner_template.transform(inner_property, source, destination, declare_type=False) | indent(4) }}
{% endfor %}
{% if ns.contains_properties_without_transform and ns.contains_modified_properties %}
else:
    {{ destination }} = {{ source }}
{%- elif ns.contains_properties_without_transform %}
{{ destination }} = {{ source }}
{%- endif %}
{% endmacro %}


{% macro transform_multipart(property, source, destination) %}
{% set ns = namespace(has_if = false) %}
{{ destination }}: {{ property.get_type_string(json=False) }}

{% if not property.required %}
if isinstance({{ source }}, Unset):
    {{ destination }} = UNSET
    {% set ns.has_if = true %}
{% endif %}
{% for inner_property in property.inner_properties %}
{% if not ns.has_if %}
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
{% set ns.has_if = true %}
{% elif not loop.last %}
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
{% else %}
else:
{% endif %}
{% import "property_templates/" + inner_property.template as inner_template %}
    {{ inner_template.transform_multipart(inner_property, source, destination) | indent(4) | trim }}
{% endfor %}
{% endmacro %}
