Skip to content

Commit 42f13a1

Browse files
authored
Merge pull request #122 from dapper91/dev
- raw element missing tail bug fixed. See #118.
2 parents 8543681 + 33cf243 commit 42f13a1

File tree

6 files changed

+27
-7
lines changed

6 files changed

+27
-7
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
2.2.3 (2023-09-20)
5+
------------------
6+
7+
- raw element missing tail bug fixed. See https://github.com/dapper91/pydantic-xml/issues/118.
8+
9+
410
2.2.2 (2023-09-15)
511
------------------
612

pydantic_xml/element/element.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,18 @@ class XmlElement(XmlElementReader, XmlElementWriter, Generic[NativeElement]):
221221
NativeElementInner = TypeVar('NativeElementInner')
222222

223223
class State(Generic[NativeElementInner]):
224-
__slots__ = ('text', 'attrib', 'elements', 'next_element_idx')
224+
__slots__ = ('text', 'tail', 'attrib', 'elements', 'next_element_idx')
225225

226226
def __init__(
227227
self,
228228
text: Optional[str],
229+
tail: Optional[str],
229230
attrib: Optional[Dict[str, str]],
230231
elements: List['XmlElement[XmlElement.NativeElementInner]'],
231232
next_element_idx: int,
232233
):
233234
self.text = text
235+
self.tail = tail
234236
self.attrib = attrib
235237
self.elements = elements
236238
self.next_element_idx = next_element_idx
@@ -259,6 +261,7 @@ def __init__(
259261
self,
260262
tag: str,
261263
text: Optional[str] = None,
264+
tail: Optional[str] = None,
262265
attributes: Optional[Dict[str, str]] = None,
263266
elements: Optional[List['XmlElement[NativeElement]']] = None,
264267
nsmap: Optional[NsMap] = None,
@@ -267,6 +270,7 @@ def __init__(
267270
self._nsmap = nsmap
268271
self._state = XmlElement.State(
269272
text=text,
273+
tail=tail,
270274
attrib=dict(attributes) if attributes is not None else None,
271275
elements=elements or [],
272276
next_element_idx=0,
@@ -280,6 +284,7 @@ def create_snapshot(self) -> 'XmlElement[NativeElement]':
280284
element = self.__class__(
281285
tag=self._tag,
282286
text=self._state.text,
287+
tail=self._state.tail,
283288
attributes=dict(self._state.attrib) if self._state.attrib is not None else None,
284289
elements=[element.create_snapshot() for element in self._state.elements],
285290
nsmap=dict(self._nsmap) if self._nsmap is not None else None,
@@ -292,12 +297,13 @@ def apply_snapshot(self, snapshot: 'XmlElement[NativeElement]') -> None:
292297
self._tag = snapshot._tag
293298
self._nsmap = snapshot._nsmap
294299
self._state.text = snapshot._state.text
300+
self._state.tail = snapshot._state.tail
295301
self._state.attrib = snapshot._state.attrib
296302
self._state.elements = snapshot._state.elements
297303
self._state.next_element_idx = snapshot._state.next_element_idx
298304

299305
def is_empty(self) -> bool:
300-
if not self._state.text and not self._state.attrib and len(self._state.elements) == 0:
306+
if not self._state.text and not self._state.tail and not self._state.attrib and len(self._state.elements) == 0:
301307
return True
302308
else:
303309
return False
@@ -379,6 +385,9 @@ def get_unbound(self, path: Tuple[str, ...] = ()) -> List[Tuple[Tuple[str, ...],
379385
if self._state.text and (text := self._state.text.strip()):
380386
result.append((path, text))
381387

388+
if self._state.tail and (tail := self._state.tail.strip()):
389+
result.append((path, tail))
390+
382391
if attrs := self._state.attrib:
383392
for name, value in attrs.items():
384393
result.append((path + (f'@{name}',), value))

pydantic_xml/element/native/lxml.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def from_native(cls, element: ElementT) -> 'XmlElement':
2020
return cls(
2121
tag=element.tag,
2222
text=element.text,
23+
tail=element.tail,
2324
attributes={
2425
force_str(name): force_str(value) # transformation is safe since lxml bytes values are ASCII compatible
2526
for name, value in element.attrib.items()
@@ -39,6 +40,7 @@ def to_native(self) -> ElementT:
3940
nsmap={ns or None: uri for ns, uri in self._nsmap.items()} if self._nsmap else None, # type: ignore[misc]
4041
)
4142
element.text = self._state.text
43+
element.tail = self._state.tail
4244
element.extend([element.to_native() for element in self._state.elements])
4345

4446
return element

pydantic_xml/element/native/std.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def from_native(cls, element: ElementT) -> 'XmlElement':
1919
return cls(
2020
tag=element.tag,
2121
text=element.text,
22+
tail=element.tail,
2223
attributes=dict(element.attrib),
2324
elements=[
2425
XmlElement.from_native(sub_element)
@@ -30,6 +31,7 @@ def from_native(cls, element: ElementT) -> 'XmlElement':
3031
def to_native(self) -> ElementT:
3132
element = etree.Element(self._tag, attrib=self._state.attrib or {})
3233
element.text = self._state.text
34+
element.tail = self._state.tail
3335
element.extend([element.to_native() for element in self._state.elements])
3436

3537
return element

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pydantic-xml"
3-
version = "2.2.2"
3+
version = "2.2.3"
44
description = "pydantic xml extension"
55
authors = ["Dmitry Pershin <dapper1291@gmail.com>"]
66
license = "Unlicense"

tests/test_raw.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ class TestModel(BaseXmlModel, tag='model', arbitrary_types_allowed=True):
1414
xml = '''
1515
<model>
1616
<element1 attr1="1">text</element1>
17-
<element2>
18-
<sub-element1 />
19-
</element2>
17+
<element2><sub-element1 />tail</element2>
2018
</model>
2119
'''
2220

@@ -29,12 +27,15 @@ class TestModel(BaseXmlModel, tag='model', arbitrary_types_allowed=True):
2927
sub_elements = list(actual_obj.element2)
3028
assert len(sub_elements) == 1
3129
assert sub_elements[0].tag == 'sub-element1'
30+
assert sub_elements[0].tail == 'tail'
3231

3332
element1 = etree.Element('element1', attr1='1')
3433
element1.text = 'text'
3534

3635
element2 = etree.Element('element2')
37-
element2.append(etree.Element('sub-element1'))
36+
sub_element = etree.Element('sub-element1')
37+
sub_element.tail = 'tail'
38+
element2.append(sub_element)
3839

3940
actual_obj = TestModel(element1=element1, element2=element2)
4041
actual_xml = actual_obj.to_xml()

0 commit comments

Comments
 (0)