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

1"""XmlElement implementation.""" 

2 

3from typing import Optional 

4import xml.etree.ElementPath as ElementPath 

5 

6import gws 

7 

8from . import namespace, serializer 

9 

10 

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 = [] 

18 

19 # extensions 

20 

21 pname = namespace.unqualify_name(tag) 

22 self.name = pname 

23 self.lcName = pname.lower() 

24 

25 # ElementTree.Element implementations, copied from Python 3.11 ElementTree.py 

26 

27 def __repr__(self): 

28 return '<%s %r at %#x>' % (self.__class__.__name__, self.tag, id(self)) 

29 

30 def makeelement(self, tag, attrib): 

31 return self.__class__(tag, attrib) 

32 

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 

39 

40 def __len__(self): 

41 return len(self._children) 

42 

43 def __getitem__(self, index): 

44 return self._children[index] 

45 

46 def __setitem__(self, index, element): 

47 self._children[index] = element 

48 

49 def __delitem__(self, index): 

50 del self._children[index] 

51 

52 def append(self, subelement): 

53 self._children.append(subelement) 

54 

55 def extend(self, elements): 

56 for element in elements: 

57 self._children.append(element) 

58 

59 def insert(self, index, subelement): 

60 self._children.insert(index, subelement) 

61 

62 def remove(self, subelement): 

63 self._children.remove(subelement) 

64 

65 def find(self, path, namespaces=None): 

66 return ElementPath.find(self, path, namespaces) 

67 

68 def findtext(self, path, default=None, namespaces=None): 

69 return ElementPath.findtext(self, path, default, namespaces) 

70 

71 def findall(self, path, namespaces=None): 

72 return ElementPath.findall(self, path, namespaces) 

73 

74 def iterfind(self, path, namespaces=None): 

75 return ElementPath.iterfind(self, path, namespaces) 

76 

77 def clear(self): 

78 self.attrib = {} 

79 self._children = [] 

80 self.text = self.tail = '' 

81 

82 def get(self, key, default=None): 

83 return self.attrib.get(key, default) 

84 

85 def set(self, key, value): 

86 self.attrib[key] = value 

87 

88 def keys(self): 

89 return self.attrib.keys() 

90 

91 def items(self): 

92 return self.attrib.items() 

93 

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) 

101 

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 

114 

115 ## extensions 

116 

117 def __bool__(self): 

118 return True 

119 

120 def __iter__(self): 

121 for c in self._children: 

122 yield c 

123 

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 

129 

130 def children(self): 

131 return self._children 

132 

133 def has(self, key): 

134 return key in self.attrib 

135 

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 } 

144 

145 def to_list(self, opts=None): 

146 ser = serializer.Serializer(self, opts=opts) 

147 return ser.to_list() 

148 

149 def to_string(self, opts=None): 

150 ser = serializer.Serializer(self, opts=opts) 

151 return ser.to_string() 

152 

153 def to_str(self, opts=None): 

154 ser = serializer.Serializer(self, opts=opts) 

155 return ser.to_string() 

156 

157 ## 

158 

159 def add(self, tag, attrib=None, **extra): 

160 el = self.__class__(tag, attrib or {}, **extra) 

161 self.append(el) 

162 return el 

163 

164 def attr(self, key, default=''): 

165 return self.get(key, default) 

166 

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 

174 

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 

180 

181 def textlist(self, *paths, deep=False): 

182 ls = self._collect_tags_and_text(paths, deep) 

183 return [text for _, text in ls] 

184 

185 def textdict(self, *paths, deep=False): 

186 ls = self._collect_tags_and_text(paths, deep) 

187 return dict(ls) 

188 

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 

199 

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) 

208 

209 ls = [] 

210 

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) 

218 

219 return ls