dapper91 / pydantic-xml

python xml for humans
https://pydantic-xml.readthedocs.io
The Unlicense
141 stars 14 forks source link

Submodels instantiated as XmlEntityInfo instead of XmlModelMeta #191

Closed RiccardoNizzolo closed 4 days ago

RiccardoNizzolo commented 1 month ago

Hi I'm migrating a library from pydantic_xml 0.6 to the most recent version. I see that the subclasses are instantiated with different logic compared to the older version. Indeed running this script with different pydantic_xml version return different results. here the code:

from typing import List, Optional
from pydantic_xml import BaseXmlModel, element, attr
import pkg_resources
ver = pkg_resources.get_distribution("pydantic-xml").version

class Product(BaseXmlModel):
    launched: Optional[int] = attr(default=None)

class Company(BaseXmlModel):

    products: List[Product] = element(tag='products')
    company_name: str = element(tag='company_name')

print(type(Company))
print(type(Product))
if ver == '0.6.0': #adaptation according to new api
    print(type(getattr(Company.__fields__['products'], 'type_')))
    print(type(getattr(Company.__fields__['company_name'], 'type_')))
else:
    print(type(Company.model_fields['products']))
    print(type(Company.model_fields['company_name']))

output with version 0.6.0

<class 'pydantic_xml.model.XmlModelMeta'>
<class 'pydantic_xml.model.XmlModelMeta'>
<class 'pydantic_xml.model.XmlModelMeta'># the subclass for products is an XmlModelMeta, therefore is not a standard subfield
<class 'type'>

output with version 2.10.0

<class 'pydantic_xml.model.XmlModelMeta'>
<class 'pydantic_xml.model.XmlModelMeta'>
<class 'pydantic_xml.model.XmlEntityInfo'># the submodel is not navigable anymore because is instantiated as XmlEntityInfo
<class 'pydantic_xml.model.XmlEntityInfo'>

Could this issue be a bug? How can I distinguish between a basic type that has no inner fields and a submodel that includes fields?

dapper91 commented 1 month ago

@RiccardoNizzolo Hi,

That is breaking change in pydantic itself, not pydantic-xml. Starting from pydantic 2.0 it is not possible to get the field type from __fields__ map anymore.

If you want to analyze fields by yourself, you should analyze pydantic core schema:

>>> pprint(Company.__pydantic_core_schema__)
{'cls': <class '__main__.Company'>,
 'config': {'title': 'Company'},
 'custom_init': False,
 'metadata': {...},
 'ref': '__main__.Company:140461506687856',
 'root_model': False,
 'schema': {'computed_fields': [],
            'fields': {'company_name': {'metadata': ...,
                                        'schema': {'type': 'str'},
                                        'type': 'model-field'},
                       'products': {'metadata': ...,
                                    'schema': {'items_schema': {'cls': <class '__main__.Product'>,
                                                                'config': {'title': 'Product'},
                                                                'custom_init': False,
                                                                'metadata': ...,
                                                                'ref': '__main__.Product:140461504455808',
                                                                'root_model': False,
                                                                'schema': ...,
                                                                'type': 'model'},
                                               'type': 'list'},
                                    'type': 'model-field'}},
            'model_name': 'Company',
            'type': 'model-fields'},
 'type': 'model'}

as you can see schema->fields->company_name->schema->type is str, schema->fields->products->schema->items_schema->type is model.

You can find more information here.