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

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 children(self): 

125 return self._children 

126 

127 def has(self, key): 

128 return key in self.attrib 

129 

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 } 

138 

139 def to_list(self, opts=None): 

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

141 return ser.to_list() 

142 

143 def to_string(self, opts=None): 

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

145 return ser.to_string() 

146 

147 def to_str(self, opts=None): 

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

149 return ser.to_string() 

150 

151 ## 

152 

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

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

155 self.append(el) 

156 return el 

157 

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

159 return self.get(key, default) 

160 

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 

168 

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 

174 

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

176 ls = self._collect_tags_and_text(paths, deep) 

177 return [text for _, text in ls] 

178 

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

180 ls = self._collect_tags_and_text(paths, deep) 

181 return dict(ls) 

182 

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 

193 

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) 

202 

203 ls = [] 

204 

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) 

212 

213 return ls