Coverage for gws-app / gws / lib / xmlx / element.py: 90%
155 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
1"""XmlElement implementation."""
3from typing import Optional
4import xml.etree.ElementPath as ElementPath
6import gws
8from . import namespace, serializer
11class XmlElement(gws.XmlElement):
12 def __init__(self, tag: str, attrib: Optional[dict] = None, **extra):
13 self.tag = tag
14 self.text = ''
15 self.tail = ''
16 self.attrib = {**(attrib or {}), **extra}
17 self._children = []
19 # extensions
21 pname = namespace.unqualify_name(tag)
22 self.name = pname
23 self.lcName = pname.lower()
25 # ElementTree.Element implementations, copied from Python 3.11 ElementTree.py
27 def __repr__(self):
28 return '<%s %r at %#x>' % (self.__class__.__name__, self.tag, id(self))
30 def makeelement(self, tag, attrib):
31 return self.__class__(tag, attrib)
33 def __copy__(self):
34 elem = self.__class__(self.tag, self.attrib)
35 elem.text = self.text
36 elem.tail = self.tail
37 elem._children = list(self._children)
38 return elem
40 def __len__(self):
41 return len(self._children)
43 def __getitem__(self, index):
44 return self._children[index]
46 def __setitem__(self, index, element):
47 self._children[index] = element
49 def __delitem__(self, index):
50 del self._children[index]
52 def append(self, subelement):
53 self._children.append(subelement)
55 def extend(self, elements):
56 for element in elements:
57 self._children.append(element)
59 def insert(self, index, subelement):
60 self._children.insert(index, subelement)
62 def remove(self, subelement):
63 self._children.remove(subelement)
65 def find(self, path, namespaces=None):
66 return ElementPath.find(self, path, namespaces)
68 def findtext(self, path, default=None, namespaces=None):
69 return ElementPath.findtext(self, path, default, namespaces)
71 def findall(self, path, namespaces=None):
72 return ElementPath.findall(self, path, namespaces)
74 def iterfind(self, path, namespaces=None):
75 return ElementPath.iterfind(self, path, namespaces)
77 def clear(self):
78 self.attrib = {}
79 self._children = []
80 self.text = self.tail = ''
82 def get(self, key, default=None):
83 return self.attrib.get(key, default)
85 def set(self, key, value):
86 self.attrib[key] = value
88 def keys(self):
89 return self.attrib.keys()
91 def items(self):
92 return self.attrib.items()
94 def iter(self, tag=None):
95 if tag == '*':
96 tag = None
97 if tag is None or self.tag == tag:
98 yield self
99 for e in self._children:
100 yield from e.iter(tag)
102 def itertext(self):
103 tag = self.tag
104 if not isinstance(tag, str) and tag is not None:
105 return
106 t = self.text
107 if t:
108 yield t
109 for e in self:
110 yield from e.itertext()
111 t = e.tail
112 if t:
113 yield t
115 ## extensions
117 def __bool__(self):
118 return True
120 def __iter__(self):
121 for c in self._children:
122 yield c
124 def require(self, path, namespaces=None):
125 el = self.find(path, namespaces)
126 if el is None:
127 raise gws.Error(f'XmlElement: required element not found: {path!r}')
128 return el
130 def children(self):
131 return self._children
133 def has(self, key):
134 return key in self.attrib
136 def to_dict(self):
137 return {
138 'tag': self.tag,
139 'attrib': self.attrib,
140 'text': self.text,
141 'tail': self.tail,
142 'children': [c.to_dict() for c in self.children()],
143 }
145 def to_list(self, opts=None):
146 ser = serializer.Serializer(self, opts=opts)
147 return ser.to_list()
149 def to_string(self, opts=None):
150 ser = serializer.Serializer(self, opts=opts)
151 return ser.to_string()
153 def to_str(self, opts=None):
154 ser = serializer.Serializer(self, opts=opts)
155 return ser.to_string()
157 ##
159 def add(self, tag, attrib=None, **extra):
160 el = self.__class__(tag, attrib or {}, **extra)
161 self.append(el)
162 return el
164 def attr(self, key, default=''):
165 return self.get(key, default)
167 def findfirst(self, *paths):
168 if not paths:
169 return self._children[0] if len(self._children) > 0 else None
170 for path in paths:
171 el = self.find(path)
172 if el is not None:
173 return el
175 def textof(self, *paths):
176 for path in paths:
177 el = self.find(path)
178 if el is not None and el.text:
179 return el.text
181 def textlist(self, *paths, deep=False):
182 ls = self._collect_tags_and_text(paths, deep)
183 return [text for _, text in ls]
185 def textdict(self, *paths, deep=False):
186 ls = self._collect_tags_and_text(paths, deep)
187 return dict(ls)
189 def remove_namespaces(self):
190 self.tag = namespace.unqualify_name(self.tag)
191 attrib = {}
192 for k, v in self.attrib.items():
193 nk = namespace.unqualify_name(k)
194 attrib[nk] = v
195 self.attrib = attrib
196 for c in self._children:
197 c.remove_namespaces()
198 return self
200 def _collect_tags_and_text(self, paths, deep):
201 def walk(el):
202 s = (el.text or '').strip()
203 if s:
204 ls.append((el.tag, s))
205 if deep:
206 for c in el:
207 walk(c)
209 ls = []
211 if not paths:
212 for el in self:
213 walk(el)
214 else:
215 for path in paths:
216 for el in self.findall(path):
217 walk(el)
219 return ls