44
55from pydantic_xml .typedefs import NsMap
66
7+ PathElementT = TypeVar ('PathElementT' )
8+ PathT = Tuple [PathElementT , ...]
9+
710
811class XmlElementReader (abc .ABC ):
912 """
@@ -90,7 +93,7 @@ def pop_element(self, tag: str, search_mode: 'SearchMode') -> Optional['XmlEleme
9093 """
9194
9295 @abc .abstractmethod
93- def find_sub_element (self , path : Sequence [str ], search_mode : 'SearchMode' ) -> Optional ['XmlElementReader' ]:
96+ def find_sub_element (self , path : Sequence [str ], search_mode : 'SearchMode' ) -> PathT ['XmlElementReader' ]:
9497 """
9598 Searches for an element at the provided path. If the element is not found returns `None`.
9699
@@ -122,13 +125,21 @@ def to_native(self) -> Any:
122125 """
123126
124127 @abc .abstractmethod
125- def get_unbound (self ) -> List [Tuple [Tuple [ str , ... ], str ]]:
128+ def get_unbound (self ) -> List [Tuple [PathT [ 'XmlElementReader' ], Optional [ str ], str ]]:
126129 """
127130 Returns unbound entities.
128131
129132 :return: list of unbound entities
130133 """
131134
135+ @abc .abstractmethod
136+ def get_sourceline (self ) -> int :
137+ """
138+ Returns source line of the element in the xml document.
139+
140+ :return: source line
141+ """
142+
132143
133144class XmlElementWriter (abc .ABC ):
134145 """
@@ -265,6 +276,7 @@ def __init__(
265276 attributes : Optional [Dict [str , str ]] = None ,
266277 elements : Optional [List ['XmlElement[NativeElement]' ]] = None ,
267278 nsmap : Optional [NsMap ] = None ,
279+ sourceline : int = - 1 ,
268280 ):
269281 self ._tag = tag
270282 self ._nsmap = nsmap
@@ -275,6 +287,11 @@ def __init__(
275287 elements = elements or [],
276288 next_element_idx = 0 ,
277289 )
290+ self ._sourceline = sourceline
291+
292+ @abc .abstractmethod
293+ def get_sourceline (self ) -> int :
294+ return self ._sourceline
278295
279296 @property
280297 def tag (self ) -> str :
@@ -345,15 +362,17 @@ def pop_element(self, tag: str, search_mode: 'SearchMode') -> Optional['XmlEleme
345362
346363 return searcher (self ._state , tag , False , True )
347364
348- def find_sub_element (self , path : Sequence [str ], search_mode : 'SearchMode' ) -> Optional ['XmlElement[NativeElement]' ]:
365+ def find_sub_element (self , path : Sequence [str ], search_mode : 'SearchMode' ) -> PathT ['XmlElement[NativeElement]' ]:
349366 assert len (path ) > 0 , "path can't be empty"
350367
351- root , path = path [0 ], path [1 :]
352- element = self .find_element (root , search_mode )
353- if element and path :
354- return element .find_sub_element (path , search_mode )
355-
356- return element
368+ root , * path = path
369+ if (element := self .find_element (root , search_mode )) is not None :
370+ if path :
371+ return (element ,) + element .find_sub_element (path , search_mode )
372+ else :
373+ return (element ,)
374+ else :
375+ return ()
357376
358377 def find_element_or_create (
359378 self ,
@@ -379,21 +398,24 @@ def find_element(
379398
380399 return searcher (self ._state , tag , look_behind , step_forward )
381400
382- def get_unbound (self , path : Tuple [str , ...] = ()) -> List [Tuple [Tuple [str , ...], str ]]:
383- result : List [Tuple [Tuple [str , ...], str ]] = []
401+ def get_unbound (
402+ self ,
403+ path : PathT [XmlElementReader ] = (),
404+ ) -> List [Tuple [PathT [XmlElementReader ], Optional [str ], str ]]:
405+ result : List [Tuple [PathT [XmlElementReader ], Optional [str ], str ]] = []
384406
385407 if self ._state .text and (text := self ._state .text .strip ()):
386- result .append ((path , text ))
408+ result .append ((path , None , text ))
387409
388410 if self ._state .tail and (tail := self ._state .tail .strip ()):
389- result .append ((path , tail ))
411+ result .append ((path , None , tail ))
390412
391413 if attrs := self ._state .attrib :
392414 for name , value in attrs .items ():
393- result .append ((path + ( f'@ { name } ' ,) , value ))
415+ result .append ((path , name , value ))
394416
395417 for sub_element in self ._state .elements :
396- result .extend (sub_element .get_unbound (path + (sub_element . tag ,)))
418+ result .extend (sub_element .get_unbound (path + (sub_element ,)))
397419
398420 return result
399421
0 commit comments