Closed bjones1 closed 9 months ago
Hi @bjones1
Thank you for the feedback.
I decided it is better not to add a new search_mode
but respect extra='forbid'
pydantic feature.
Since v2.2.0 it raises an exception if extra='forbid'
and the document contains an extra element/attribute/text.
The following example illustrate that:
class Model(BaseXmlModel, tag='root', extra='forbid', search_mode='unordered'):
attr1: str = attr()
field1: str = element()
xml = '''
<root attr1="attr value 1" attr2="attr value 2">
<field1>field value 1</field1>
<field2>field value 2</field2>
</root>
'''
try:
Model.from_xml(xml)
except pd.ValidationError as err:
assert err.errors() == [
{
'type': 'extra_forbidden',
'msg': 'Extra inputs are not permitted',
'input': 'attr value 2',
'loc': ('<attr> attr2',),
},
{
'type': 'extra_forbidden',
'msg': 'Extra inputs are not permitted',
'input': 'field value 2',
'loc': ('<element> field2',),
},
]
Wow, this is fantastic! It's a much cleaner approach than adding an additional search_mode
. I was already using extra="forbid"
in my models, so this was a very simple change for me. Thank you!
However, it appears that validation errors for extra attributes don't get raised for wrapped items. Am I missing something?
from pydantic_xml import BaseXmlModel, attr, element, wrapped
import pydantic as pd
class Model(BaseXmlModel, tag="root", extra="forbid", search_mode="unordered"):
attr1: str = attr()
field1: str = wrapped("wrapper", element())
xml = """
<root attr1="attr value 1" attr2="attr value 2">
<wrapper>
<field1>field value 1</field1>
<field2>field value 2</field2>
</wrapper>
</root>
"""
try:
Model.from_xml(xml)
except pd.ValidationError as err:
assert err.errors() == [
{
"type": "extra_forbidden",
"msg": "Extra inputs are not permitted",
"input": "attr value 2",
"loc": ("<attr> attr2",),
"url": "https://errors.pydantic.dev/2.3/v/extra_forbidden",
},
{
"type": "extra_forbidden",
"msg": "Extra inputs are not permitted",
"input": "field value 2",
"loc": ("<element> field2",),
"url": "https://errors.pydantic.dev/2.3/v/extra_forbidden",
},
]
My output from running this (note the missing validation error for <field2>
:
Traceback (most recent call last):
File "C:\Users\bjones\documents\git\pretext-cli\test-fix.py", line 20, in <module>
Model.from_xml(xml)
File "C:\Users\bjones\documents\git\pretext-cli\.venv\Lib\site-packages\pydantic_xml\model.py", line 343, in from_xml
return cls.from_xml_tree(etree.fromstring(source), context=context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\bjones\documents\git\pretext-cli\.venv\Lib\site-packages\pydantic_xml\model.py", line 326, in from_xml_tree
obj = typing.cast(ModelT, cls.__xml_serializer__.deserialize(XmlElement.from_native(root), context=context))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\bjones\documents\git\pretext-cli\.venv\Lib\site-packages\pydantic_xml\serializers\factories\model.py", line 198, in deserialize
self._check_extra(self._model.__name__, element)
File "C:\Users\bjones\documents\git\pretext-cli\.venv\Lib\site-packages\pydantic_xml\serializers\factories\model.py", line 63, in _check_extra
raise pd.ValidationError.from_exception_data(title=error_title, line_errors=line_errors)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Model
<attr> attr2
Extra inputs are not permitted [type=extra_forbidden, input_value='attr value 2', input_type=str]
For further information visit https://errors.pydantic.dev/2.3/v/extra_forbidden
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\bjones\documents\git\pretext-cli\test-fix.py", line 23, in <module>
assert err.errors() == [
^^^^^^^^^^^^^^^^^
AssertionError
Forgot about wrapped items. FIxed it in 2.2.1:
from pydantic_xml import BaseXmlModel, attr, element, wrapped
import pydantic as pd
class Model(BaseXmlModel, tag="root", extra="forbid", search_mode="unordered"):
attr1: str = attr()
field1: str = wrapped("wrapper", element())
xml = """
<root attr1="attr value 1" attr2="attr value 2">
<wrapper>
<field1>field value 1</field1>
<field2>field value 2</field2>
</wrapper>
</root>
"""
try:
Model.from_xml(xml)
except pd.ValidationError as err:
assert err.errors() == [
{
"type": "extra_forbidden",
"msg": "Extra inputs are not permitted",
"input": "attr value 2",
"loc": ("@attr2",),
"url": "https://errors.pydantic.dev/2.3/v/extra_forbidden",
},
{
"type": "extra_forbidden",
"msg": "Extra inputs are not permitted",
"input": "field value 2",
"loc": ("wrapper", "field2",),
"url": "https://errors.pydantic.dev/2.3/v/extra_forbidden",
},
]
Fantastic! All my tests pass now -- thanks again!
Thanks for a great library! I'm really excited about and enjoying the v2 interface. The docs are excellent, which is very helpful.
I'd like to have an additional validation element search mode. I use your library to read in a user-written, XML-formatted config file, so I select the Unordered mode. However, I'd like to raise a validation error if an element isn't in the model, like strict mode, but without the ordering requirements imposed by strict mode.
Is this possible? If so, I'd certainly appreciate this feature.
Thanks!