Skip to content

Recursive tagged unions fail to create serializer #278

@DenSinH

Description

@DenSinH

Hello again,

We have stumbled upon another interesting edge case: we are trying to load a model which has a self-referential tagged union. I made a small example that fails:

from typing import Literal, Annotated

from pydantic_xml import BaseXmlModel, element, attr
from pydantic import Field


class Other(BaseXmlModel, tag="other"):
    name: Literal["other"] = attr(default="other")


class Test(BaseXmlModel, tag="test"):
    name: Literal["hi"] = attr(default="hi")
    nested: list[Annotated["Test | Other", Field(discriminator="name")]] = element(default=None)


text = """
    <test name="hi">
    <nested name="hi">
    </nested>
    <nested name="hi">
    </nested>
    <nested name="hi">
    </nested>
    <nested name="hi">
    </nested>
    </test>
"""
print(Test.from_xml(text))

The expecte behavior is that this just loads™. Sadly, it crashes trying to construct the serializer for Test:

Traceback (most recent call last):
  File "C:\projects\playground\mdsectioning.py", line 11, in <module>
    class Test(BaseXmlModel, tag="test"):
  File "C:\projects\playground\pydantic-xml\pydantic_xml\model.py", line 387, in __new__
    cls.__build_serializer__()
  File "C:\projects\playground\pydantic-xml\pydantic_xml\model.py", line 494, in __build_serializer__
    serializer = Serializer.parse_core_schema(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 184, in parse_core_schema
    return cls.select_serializer(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 241, in select_serializer
    return factories.model.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\model.py", line 453, in from_core_schema
    return ModelSerializer.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\model.py", line 91, in from_core_schema
    fields_serializers[field_name] = Serializer.parse_core_schema(model_field['schema'], field_ctx)
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 184, in parse_core_schema
    return cls.select_serializer(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 245, in select_serializer
    return factories.homogeneous.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\homogeneous.py", line 133, in from_core_schema
    return ElementSerializer.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\homogeneous.py", line 31, in from_core_schema
    inner_serializer = Serializer.parse_core_schema(items_schema, ctx)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 184, in parse_core_schema
    return cls.select_serializer(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\serializer.py", line 265, in select_serializer
    return factories.tagged_union.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\tagged_union.py", line 126, in from_core_schema
    return ModelSerializer.from_core_schema(schema, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\projects\playground\pydantic-xml\pydantic_xml\serializers\factories\tagged_union.py", line 37, in from_core_schema
    assert isinstance(model_serializer, factories.model.ModelSerializer), "unexpected model serializer type"
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: unexpected model serializer type

From what I could tell, it does the following: it tries to add serializers for all possible options in the union, gets to the model itself and then gets None, as it is not created yet. I am not sure how this is solved for directly recursive models, but it does not work in this case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions