Coverage for gws-app/gws/lib/xmlx/element.py: 92%
150 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
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 children(self):
125 return self._children
127 def has(self, key):
128 return key in self.attrib
130 def to_dict(self):
131 return {
132 'tag': self.tag,
133 'attrib': self.attrib,
134 'text': self.text,
135 'tail': self.tail,
136 'children': [c.to_dict() for c in self.children()],
137 }
139 def to_list(self, opts=None):
140 ser = serializer.Serializer(self, opts=opts)
141 return ser.to_list()
143 def to_string(self, opts=None):
144 ser = serializer.Serializer(self, opts=opts)
145 return ser.to_string()
147 def to_str(self, opts=None):
148 ser = serializer.Serializer(self, opts=opts)
149 return ser.to_string()
151 ##
153 def add(self, tag, attrib=None, **extra):
154 el = self.__class__(tag, attrib or {}, **extra)
155 self.append(el)
156 return el
158 def attr(self, key, default=''):
159 return self.get(key, default)
161 def findfirst(self, *paths):
162 if not paths:
163 return self._children[0] if len(self._children) > 0 else None
164 for path in paths:
165 el = self.find(path)
166 if el is not None:
167 return el
169 def textof(self, *paths):
170 for path in paths:
171 el = self.find(path)
172 if el is not None and el.text:
173 return el.text
175 def textlist(self, *paths, deep=False):
176 ls = self._collect_tags_and_text(paths, deep)
177 return [text for _, text in ls]
179 def textdict(self, *paths, deep=False):
180 ls = self._collect_tags_and_text(paths, deep)
181 return dict(ls)
183 def remove_namespaces(self):
184 self.tag = namespace.unqualify_name(self.tag)
185 attrib = {}
186 for k, v in self.attrib.items():
187 nk = namespace.unqualify_name(k)
188 attrib[nk] = v
189 self.attrib = attrib
190 for c in self._children:
191 c.remove_namespaces()
192 return self
194 def _collect_tags_and_text(self, paths, deep):
195 def walk(el):
196 s = (el.text or '').strip()
197 if s:
198 ls.append((el.tag, s))
199 if deep:
200 for c in el:
201 walk(c)
203 ls = []
205 if not paths:
206 for el in self:
207 walk(el)
208 else:
209 for path in paths:
210 for el in self.findall(path):
211 walk(el)
213 return ls