11import dataclasses as dc
22import typing
3- from typing import Any , Callable , Optional , Union
3+ from typing import Any , Callable , Dict , Optional , Union
44
55import pydantic as pd
66import pydantic_core as pdc
7- from pydantic ._internal ._model_construction import ModelMetaclass # noqa
8- from pydantic .root_model import _RootModelMetaclass as RootModelMetaclass # noqa
97
10- from . import config , model , utils
8+ from . import compat , config , model , utils
119from .typedefs import EntityLocation
1210from .utils import NsMap
1311
1715 'computed_element' ,
1816 'computed_entity' ,
1917 'element' ,
18+ 'extract_field_xml_entity_info' ,
2019 'wrapped' ,
2120 'xml_field_serializer' ,
2221 'xml_field_validator' ,
@@ -37,83 +36,79 @@ class XmlEntityInfoP(typing.Protocol):
3736 wrapped : Optional ['XmlEntityInfoP' ]
3837
3938
40- class XmlEntityInfo (pd .fields .FieldInfo , XmlEntityInfoP ):
39+ @dc .dataclass (frozen = True )
40+ class XmlEntityInfo (XmlEntityInfoP ):
4141 """
4242 Field xml meta-information.
4343 """
4444
45- __slots__ = ('location' , 'path' , 'ns' , 'nsmap' , 'nillable' , 'wrapped' )
45+ location : Optional [EntityLocation ]
46+ path : Optional [str ] = None
47+ ns : Optional [str ] = None
48+ nsmap : Optional [NsMap ] = None
49+ nillable : Optional [bool ] = None
50+ wrapped : Optional [XmlEntityInfoP ] = None
51+
52+ def __post_init__ (self ) -> None :
53+ if config .REGISTER_NS_PREFIXES and self .nsmap :
54+ utils .register_nsmap (self .nsmap )
4655
4756 @staticmethod
48- def merge_field_infos (* field_infos : pd .fields .FieldInfo , ** overrides : Any ) -> pd .fields .FieldInfo :
49- location , path , ns , nsmap , nillable , wrapped = None , None , None , None , None , None
50-
51- for field_info in field_infos :
52- if isinstance (field_info , XmlEntityInfo ):
53- location = field_info .location if field_info .location is not None else location
54- path = field_info .path if field_info .path is not None else path
55- ns = field_info .ns if field_info .ns is not None else ns
56- nsmap = field_info .nsmap if field_info .nsmap is not None else nsmap
57- nillable = field_info .nillable if field_info .nillable is not None else nillable
58- wrapped = field_info .wrapped if field_info .wrapped is not None else wrapped
59-
60- field_info = pd .fields .FieldInfo .merge_field_infos (* field_infos , ** overrides )
61-
62- xml_entity_info = XmlEntityInfo (
63- location ,
57+ def merge (* entity_infos : XmlEntityInfoP ) -> 'XmlEntityInfo' :
58+ location : Optional [EntityLocation ] = None
59+ path : Optional [str ] = None
60+ ns : Optional [str ] = None
61+ nsmap : Optional [NsMap ] = None
62+ nillable : Optional [bool ] = None
63+ wrapped : Optional [XmlEntityInfoP ] = None
64+
65+ for entity_info in entity_infos :
66+ if entity_info .location is not None :
67+ location = entity_info .location
68+ if entity_info .wrapped is not None :
69+ wrapped = entity_info .wrapped
70+ if entity_info .path is not None :
71+ path = entity_info .path
72+ if entity_info .ns is not None :
73+ ns = entity_info .ns
74+ if entity_info .nsmap is not None :
75+ nsmap = utils .merge_nsmaps (entity_info .nsmap , nsmap )
76+ if entity_info .nillable is not None :
77+ nillable = entity_info .nillable
78+
79+ return XmlEntityInfo (
80+ location = location ,
6481 path = path ,
6582 ns = ns ,
6683 nsmap = nsmap ,
6784 nillable = nillable ,
68- wrapped = wrapped if isinstance (wrapped , XmlEntityInfo ) else None ,
69- ** field_info ._attributes_set ,
85+ wrapped = wrapped ,
7086 )
71- xml_entity_info .metadata = field_info .metadata
72-
73- return xml_entity_info
74-
75- def __init__ (
76- self ,
77- location : Optional [EntityLocation ],
78- / ,
79- path : Optional [str ] = None ,
80- ns : Optional [str ] = None ,
81- nsmap : Optional [NsMap ] = None ,
82- nillable : Optional [bool ] = None ,
83- wrapped : Optional [pd .fields .FieldInfo ] = None ,
84- ** kwargs : Any ,
85- ):
86- wrapped_metadata : list [Any ] = []
87- if wrapped is not None :
88- # copy arguments from the wrapped entity to let pydantic know how to process the field
89- for entity_field_name in utils .get_slots (wrapped ):
90- if entity_field_name in pd .fields ._FIELD_ARG_NAMES :
91- kwargs [entity_field_name ] = getattr (wrapped , entity_field_name )
92- wrapped_metadata = wrapped .metadata
93-
94- if kwargs .get ('serialization_alias' ) is None :
95- kwargs ['serialization_alias' ] = kwargs .get ('alias' )
96-
97- if kwargs .get ('validation_alias' ) is None :
98- kwargs ['validation_alias' ] = kwargs .get ('alias' )
99-
100- super ().__init__ (** kwargs )
101- self .metadata .extend (wrapped_metadata )
102-
103- self .location = location
104- self .path = path
105- self .ns = ns
106- self .nsmap = nsmap
107- self .nillable = nillable
108- self .wrapped : Optional [XmlEntityInfoP ] = wrapped if isinstance (wrapped , XmlEntityInfo ) else None
109-
110- if config .REGISTER_NS_PREFIXES and nsmap :
111- utils .register_nsmap (nsmap )
87+
88+
89+ def extract_field_xml_entity_info (field_info : pd .fields .FieldInfo ) -> Optional [XmlEntityInfoP ]:
90+ entity_info_list = list (filter (lambda meta : isinstance (meta , XmlEntityInfo ), field_info .metadata ))
91+ if entity_info_list :
92+ entity_info = XmlEntityInfo .merge (* entity_info_list )
93+ else :
94+ entity_info = None
95+
96+ return entity_info
11297
11398
11499_Unset : Any = pdc .PydanticUndefined
115100
116101
102+ def prepare_field_kwargs (kwargs : Dict [str , Any ]) -> Dict [str , Any ]:
103+ if kwargs .get ('serialization_alias' ) in (None , pdc .PydanticUndefined ):
104+ kwargs ['serialization_alias' ] = kwargs .get ('alias' )
105+
106+ if kwargs .get ('validation_alias' ) in (None , pdc .PydanticUndefined ):
107+ kwargs ['validation_alias' ] = kwargs .get ('alias' )
108+
109+ return kwargs
110+
111+
117112def attr (
118113 name : Optional [str ] = None ,
119114 ns : Optional [str ] = None ,
@@ -132,12 +127,15 @@ def attr(
132127 :param kwargs: pydantic field arguments. See :py:class:`pydantic.Field`
133128 """
134129
135- return XmlEntityInfo (
136- EntityLocation .ATTRIBUTE ,
137- path = name , ns = ns , default = default , default_factory = default_factory ,
138- ** kwargs ,
130+ kwargs = prepare_field_kwargs (kwargs )
131+
132+ field_info = pd .fields .FieldInfo (default = default , default_factory = default_factory , ** kwargs )
133+ field_info .metadata .append (
134+ XmlEntityInfo (EntityLocation .ATTRIBUTE , path = name , ns = ns ),
139135 )
140136
137+ return field_info
138+
141139
142140def element (
143141 tag : Optional [str ] = None ,
@@ -161,12 +159,15 @@ def element(
161159 :param kwargs: pydantic field arguments. See :py:class:`pydantic.Field`
162160 """
163161
164- return XmlEntityInfo (
165- EntityLocation .ELEMENT ,
166- path = tag , ns = ns , nsmap = nsmap , nillable = nillable , default = default , default_factory = default_factory ,
167- ** kwargs ,
162+ kwargs = prepare_field_kwargs (kwargs )
163+
164+ field_info = pd .fields .FieldInfo (default = default , default_factory = default_factory , ** kwargs )
165+ field_info .metadata .append (
166+ XmlEntityInfo (EntityLocation .ELEMENT , path = tag , ns = ns , nsmap = nsmap , nillable = nillable ),
168167 )
169168
169+ return field_info
170+
170171
171172def wrapped (
172173 path : str ,
@@ -190,12 +191,22 @@ def wrapped(
190191 :param kwargs: pydantic field arguments. See :py:class:`pydantic.Field`
191192 """
192193
193- return XmlEntityInfo (
194- EntityLocation .WRAPPED ,
195- path = path , ns = ns , nsmap = nsmap , wrapped = entity , default = default , default_factory = default_factory ,
196- ** kwargs ,
194+ if entity is None :
195+ wrapped_entity_info = None
196+ field_info = pd .fields .FieldInfo (default = default , default_factory = default_factory , ** kwargs )
197+ else :
198+ wrapped_entity_info = extract_field_xml_entity_info (entity )
199+ field_info = compat .merge_field_infos (
200+ pd .fields .FieldInfo (default = default , default_factory = default_factory , ** kwargs ),
201+ entity ,
202+ )
203+
204+ field_info .metadata .append (
205+ XmlEntityInfo (EntityLocation .WRAPPED , path = path , ns = ns , nsmap = nsmap , wrapped = wrapped_entity_info ),
197206 )
198207
208+ return field_info
209+
199210
200211@dc .dataclass
201212class ComputedXmlEntityInfo (pd .fields .ComputedFieldInfo , XmlEntityInfoP ):
@@ -293,7 +304,7 @@ def computed_element(
293304
294305
295306def xml_field_validator (
296- field : str , / , * fields : str
307+ field : str , / , * fields : str ,
297308) -> 'Callable[[model.ValidatorFuncT[model.ModelT]], model.ValidatorFuncT[model.ModelT]]' :
298309 """
299310 Marks the method as a field xml validator.
@@ -312,7 +323,7 @@ def wrapper(func: model.ValidatorFuncT[model.ModelT]) -> model.ValidatorFuncT[mo
312323
313324
314325def xml_field_serializer (
315- field : str , / , * fields : str
326+ field : str , / , * fields : str ,
316327) -> 'Callable[[model.SerializerFuncT[model.ModelT]], model.SerializerFuncT[model.ModelT]]' :
317328 """
318329 Marks the method as a field xml serializer.
0 commit comments