Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/descriptors/serialisable.py: 27%

161 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1# copyright openpyxl 2010-2015 

2 

3from copy import copy 

4from keyword import kwlist 

5KEYWORDS = frozenset(kwlist) 

6 

7from . import Descriptor 

8from . import _Serialiasable 

9from .sequence import ( 

10 Sequence, 

11 NestedSequence, 

12 MultiSequencePart, 

13) 

14from .namespace import namespaced 

15 

16from openpyxl.compat import safe_string 

17from openpyxl.xml.functions import ( 

18 Element, 

19 localname, 

20) 

21 

22seq_types = (list, tuple) 

23 

24class Serialisable(_Serialiasable): 

25 """ 

26 Objects can serialise to XML their attributes and child objects. 

27 The following class attributes are created by the metaclass at runtime: 

28 __attrs__ = attributes 

29 __nested__ = single-valued child treated as an attribute 

30 __elements__ = child elements 

31 """ 

32 

33 __attrs__ = None 

34 __nested__ = None 

35 __elements__ = None 

36 __namespaced__ = None 

37 

38 idx_base = 0 

39 

40 @property 

41 def tagname(self): 

42 raise(NotImplementedError) 

43 

44 namespace = None 

45 

46 @classmethod 

47 def from_tree(cls, node): 

48 """ 

49 Create object from XML 

50 """ 

51 # strip known namespaces from attributes 

52 attrib = dict(node.attrib) 

53 for key, ns in cls.__namespaced__: 53 ↛ 54line 53 didn't jump to line 54, because the loop on line 53 never started

54 if ns in attrib: 

55 attrib[key] = attrib[ns] 

56 del attrib[ns] 

57 

58 # strip attributes with unknown namespaces 

59 for key in list(attrib): 

60 if key.startswith('{'): 60 ↛ 61line 60 didn't jump to line 61, because the condition on line 60 was never true

61 del attrib[key] 

62 elif key in KEYWORDS: 62 ↛ 63line 62 didn't jump to line 63, because the condition on line 62 was never true

63 attrib["_" + key] = attrib[key] 

64 del attrib[key] 

65 elif "-" in key: 65 ↛ 66line 65 didn't jump to line 66, because the condition on line 65 was never true

66 n = key.replace("-", "_") 

67 attrib[n] = attrib[key] 

68 del attrib[key] 

69 

70 if node.text and "attr_text" in cls.__attrs__: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true

71 attrib["attr_text"] = node.text 

72 

73 for el in node: 

74 tag = localname(el) 

75 if tag in KEYWORDS: 75 ↛ 76line 75 didn't jump to line 76, because the condition on line 75 was never true

76 tag = "_" + tag 

77 desc = getattr(cls, tag, None) 

78 if desc is None or isinstance(desc, property): 78 ↛ 79line 78 didn't jump to line 79, because the condition on line 78 was never true

79 continue 

80 

81 if hasattr(desc, 'from_tree'): 

82 #descriptor manages conversion 

83 obj = desc.from_tree(el) 

84 else: 

85 if hasattr(desc.expected_type, "from_tree"): 

86 #complex type 

87 obj = desc.expected_type.from_tree(el) 

88 else: 

89 #primitive 

90 obj = el.text 

91 

92 if isinstance(desc, NestedSequence): 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

93 attrib[tag] = obj 

94 elif isinstance(desc, Sequence): 94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true

95 attrib.setdefault(tag, []) 

96 attrib[tag].append(obj) 

97 elif isinstance(desc, MultiSequencePart): 97 ↛ 98line 97 didn't jump to line 98, because the condition on line 97 was never true

98 attrib.setdefault(desc.store, []) 

99 attrib[desc.store].append(obj) 

100 else: 

101 attrib[tag] = obj 

102 

103 return cls(**attrib) 

104 

105 

106 def to_tree(self, tagname=None, idx=None, namespace=None): 

107 

108 if tagname is None: 

109 tagname = self.tagname 

110 

111 # keywords have to be masked 

112 if tagname.startswith("_"): 

113 tagname = tagname[1:] 

114 

115 tagname = namespaced(self, tagname, namespace) 

116 namespace = getattr(self, "namespace", namespace) 

117 

118 attrs = dict(self) 

119 for key, ns in self.__namespaced__: 

120 if key in attrs: 

121 attrs[ns] = attrs[key] 

122 del attrs[key] 

123 

124 el = Element(tagname, attrs) 

125 if "attr_text" in self.__attrs__: 

126 el.text = safe_string(getattr(self, "attr_text")) 

127 

128 for child_tag in self.__elements__: 

129 desc = getattr(self.__class__, child_tag, None) 

130 obj = getattr(self, child_tag) 

131 if hasattr(desc, "namespace") and hasattr(obj, 'namespace'): 

132 obj.namespace = desc.namespace 

133 

134 if isinstance(obj, seq_types): 

135 if isinstance(desc, NestedSequence): 

136 # wrap sequence in container 

137 if not obj: 

138 continue 

139 nodes = [desc.to_tree(child_tag, obj, namespace)] 

140 elif isinstance(desc, Sequence): 

141 # sequence 

142 desc.idx_base = self.idx_base 

143 nodes = (desc.to_tree(child_tag, obj, namespace)) 

144 else: # property 

145 nodes = (v.to_tree(child_tag, namespace) for v in obj) 

146 for node in nodes: 

147 el.append(node) 

148 else: 

149 if child_tag in self.__nested__: 

150 node = desc.to_tree(child_tag, obj, namespace) 

151 elif obj is None: 

152 continue 

153 else: 

154 node = obj.to_tree(child_tag) 

155 if node is not None: 

156 el.append(node) 

157 return el 

158 

159 

160 def __iter__(self): 

161 for attr in self.__attrs__: 

162 value = getattr(self, attr) 

163 if attr.startswith("_"): 

164 attr = attr[1:] 

165 elif attr != "attr_text" and "_" in attr: 

166 desc = getattr(self.__class__, attr) 

167 if getattr(desc, "hyphenated", False): 

168 attr = attr.replace("_", "-") 

169 if attr != "attr_text" and value is not None: 

170 yield attr, safe_string(value) 

171 

172 

173 def __eq__(self, other): 

174 if not self.__class__ == other.__class__: 

175 return False 

176 elif not dict(self) == dict(other): 

177 return False 

178 for el in self.__elements__: 

179 if getattr(self, el) != getattr(other, el): 

180 return False 

181 return True 

182 

183 

184 def __ne__(self, other): 

185 return not self == other 

186 

187 

188 def __repr__(self): 

189 s = u"<{0}.{1} object>\nParameters:".format( 

190 self.__module__, 

191 self.__class__.__name__ 

192 ) 

193 args = [] 

194 for k in self.__attrs__ + self.__elements__: 

195 v = getattr(self, k) 

196 if isinstance(v, Descriptor): 

197 v = None 

198 args.append(u"{0}={1}".format(k, repr(v))) 

199 args = u", ".join(args) 

200 

201 return u"\n".join([s, args]) 

202 

203 

204 def __hash__(self): 

205 fields = [] 

206 for attr in self.__attrs__ + self.__elements__: 

207 val = getattr(self, attr) 

208 if isinstance(val, list): 

209 val = tuple(val) 

210 fields.append(val) 

211 

212 return hash(tuple(fields)) 

213 

214 

215 def __add__(self, other): 

216 if type(self) != type(other): 

217 raise TypeError("Cannot combine instances of different types") 

218 vals = {} 

219 for attr in self.__attrs__: 

220 vals[attr] = getattr(self, attr) or getattr(other, attr) 

221 for el in self.__elements__: 

222 a = getattr(self, el) 

223 b = getattr(other, el) 

224 if a and b: 

225 vals[el] = a + b 

226 else: 

227 vals[el] = a or b 

228 return self.__class__(**vals) 

229 

230 

231 def __copy__(self): 

232 # serialise to xml and back to avoid shallow copies 

233 xml = self.to_tree(tagname="dummy") 

234 cp = self.__class__.from_tree(xml) 

235 # copy any non-persisted attributed 

236 for k in self.__dict__: 

237 if k not in self.__attrs__ + self.__elements__: 

238 v = copy(getattr(self, k)) 

239 setattr(cp, k, v) 

240 return cp