Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/styles/named_styles.py: 47%

140 statements  

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

1# Copyright (c) 2010-2022 openpyxl 

2 

3from openpyxl.compat import safe_string 

4 

5from openpyxl.descriptors import ( 

6 Typed, 

7 Integer, 

8 Bool, 

9 String, 

10 Sequence, 

11) 

12from openpyxl.descriptors.excel import ExtensionList 

13from openpyxl.descriptors.serialisable import Serialisable 

14 

15from .fills import PatternFill, Fill 

16from .fonts import Font 

17from .borders import Border 

18from .alignment import Alignment 

19from .protection import Protection 

20from .numbers import ( 

21 NumberFormatDescriptor, 

22 BUILTIN_FORMATS_MAX_SIZE, 

23 BUILTIN_FORMATS_REVERSE, 

24) 

25from .cell_style import ( 

26 StyleArray, 

27 CellStyle, 

28) 

29 

30 

31class NamedStyle(Serialisable): 

32 

33 """ 

34 Named and editable styles 

35 """ 

36 

37 font = Typed(expected_type=Font) 

38 fill = Typed(expected_type=Fill) 

39 border = Typed(expected_type=Border) 

40 alignment = Typed(expected_type=Alignment) 

41 number_format = NumberFormatDescriptor() 

42 protection = Typed(expected_type=Protection) 

43 builtinId = Integer(allow_none=True) 

44 hidden = Bool(allow_none=True) 

45 xfId = Integer(allow_none=True) 

46 name = String() 

47 _wb = None 

48 _style = StyleArray() 

49 

50 

51 def __init__(self, 

52 name="Normal", 

53 font=Font(), 

54 fill=PatternFill(), 

55 border=Border(), 

56 alignment=Alignment(), 

57 number_format=None, 

58 protection=Protection(), 

59 builtinId=None, 

60 hidden=False, 

61 xfId=None, 

62 ): 

63 self.name = name 

64 self.font = font 

65 self.fill = fill 

66 self.border = border 

67 self.alignment = alignment 

68 self.number_format = number_format 

69 self.protection = protection 

70 self.builtinId = builtinId 

71 self.hidden = hidden 

72 self._wb = None 

73 self._style = StyleArray() 

74 

75 

76 def __setattr__(self, attr, value): 

77 super(NamedStyle, self).__setattr__(attr, value) 

78 if getattr(self, '_wb', None) and attr in ( 78 ↛ 81line 78 didn't jump to line 81, because the condition on line 78 was never true

79 'font', 'fill', 'border', 'alignment', 'number_format', 'protection', 

80 ): 

81 self._recalculate() 

82 

83 

84 def __iter__(self): 

85 for key in ('name', 'builtinId', 'hidden', 'xfId'): 

86 value = getattr(self, key, None) 

87 if value is not None: 

88 yield key, safe_string(value) 

89 

90 

91 @property 

92 def xfId(self): 

93 """ 

94 Index of the style in the list of named styles 

95 """ 

96 return self._style.xfId 

97 

98 

99 def _set_index(self, idx): 

100 """ 

101 Allow the containing list to set the index 

102 """ 

103 self._style.xfId = idx 

104 

105 

106 def bind(self, wb): 

107 """ 

108 Bind a named style to a workbook 

109 """ 

110 self._wb = wb 

111 self._recalculate() 

112 

113 

114 def _recalculate(self): 

115 self._style.fontId = self._wb._fonts.add(self.font) 

116 self._style.borderId = self._wb._borders.add(self.border) 

117 self._style.fillId = self._wb._fills.add(self.fill) 

118 self._style.protectionId = self._wb._protections.add(self.protection) 

119 self._style.alignmentId = self._wb._alignments.add(self.alignment) 

120 fmt = self.number_format 

121 if fmt in BUILTIN_FORMATS_REVERSE: 

122 fmt = BUILTIN_FORMATS_REVERSE[fmt] 

123 else: 

124 fmt = self._wb._number_formats.add(self.number_format) + ( 

125 BUILTIN_FORMATS_MAX_SIZE) 

126 self._style.numFmtId = fmt 

127 

128 

129 def as_tuple(self): 

130 """Return a style array representing the current style""" 

131 return self._style 

132 

133 

134 def as_xf(self): 

135 """ 

136 Return equivalent XfStyle 

137 """ 

138 xf = CellStyle.from_array(self._style) 

139 xf.xfId = None 

140 xf.pivotButton = None 

141 xf.quotePrefix = None 

142 if self.alignment != Alignment(): 

143 xf.alignment = self.alignment 

144 if self.protection != Protection(): 

145 xf.protection = self.protection 

146 return xf 

147 

148 

149 def as_name(self): 

150 """ 

151 Return relevant named style 

152 

153 """ 

154 named = _NamedCellStyle( 

155 name=self.name, 

156 builtinId=self.builtinId, 

157 hidden=self.hidden, 

158 xfId=self.xfId 

159 ) 

160 return named 

161 

162 

163class NamedStyleList(list): 

164 """ 

165 Named styles are editable and can be applied to multiple objects 

166 

167 As only the index is stored in referencing objects the order mus 

168 be preserved. 

169 """ 

170 

171 @property 

172 def names(self): 

173 return [s.name for s in self] 

174 

175 

176 def __getitem__(self, key): 

177 if isinstance(key, int): 

178 return super(NamedStyleList, self).__getitem__(key) 

179 

180 names = self.names 

181 if key not in names: 

182 raise KeyError("No named style with the name{0} exists".format(key)) 

183 

184 for idx, name in enumerate(names): 

185 if name == key: 

186 return self[idx] 

187 

188 

189 def append(self, style): 

190 if not isinstance(style, NamedStyle): 

191 raise TypeError("""Only NamedStyle instances can be added""") 

192 elif style.name in self.names: 

193 raise ValueError("""Style {0} exists already""".format(style.name)) 

194 style._set_index(len(self)) 

195 super(NamedStyleList, self).append(style) 

196 

197 

198class _NamedCellStyle(Serialisable): 

199 

200 """ 

201 Pointer-based representation of named styles in XML 

202 xfId refers to the corresponding CellStyleXfs 

203 

204 Not used in client code. 

205 """ 

206 

207 tagname = "cellStyle" 

208 

209 name = String() 

210 xfId = Integer() 

211 builtinId = Integer(allow_none=True) 

212 iLevel = Integer(allow_none=True) 

213 hidden = Bool(allow_none=True) 

214 customBuiltin = Bool(allow_none=True) 

215 extLst = Typed(expected_type=ExtensionList, allow_none=True) 

216 

217 __elements__ = () 

218 

219 

220 def __init__(self, 

221 name=None, 

222 xfId=None, 

223 builtinId=None, 

224 iLevel=None, 

225 hidden=None, 

226 customBuiltin=None, 

227 extLst=None, 

228 ): 

229 self.name = name 

230 self.xfId = xfId 

231 self.builtinId = builtinId 

232 self.iLevel = iLevel 

233 self.hidden = hidden 

234 self.customBuiltin = customBuiltin 

235 

236 

237class _NamedCellStyleList(Serialisable): 

238 """ 

239 Container for named cell style objects 

240 

241 Not used in client code 

242 """ 

243 

244 tagname = "cellStyles" 

245 

246 count = Integer(allow_none=True) 

247 cellStyle = Sequence(expected_type=_NamedCellStyle) 

248 

249 __attrs__ = ("count",) 

250 

251 def __init__(self, 

252 count=None, 

253 cellStyle=(), 

254 ): 

255 self.cellStyle = cellStyle 

256 

257 

258 @property 

259 def count(self): 

260 return len(self.cellStyle) 

261 

262 

263 @property 

264 def names(self): 

265 """ 

266 Convert to NamedStyle objects and remove duplicates. 

267 

268 In theory the highest xfId wins but in practice they are duplicates 

269 so it doesn't matter. 

270 """ 

271 

272 def sort_fn(v): 

273 return v.xfId 

274 

275 styles = [] 

276 names = set() 

277 

278 for ns in sorted(self.cellStyle, key=sort_fn): 

279 if ns.name in names: 

280 continue 

281 

282 style = NamedStyle( 

283 name=ns.name, 

284 hidden=ns.hidden, 

285 builtinId = ns.builtinId 

286 ) 

287 names.add(ns.name) 

288 style._set_index(len(styles)) # assign xfId 

289 styles.append(style) 

290 

291 return NamedStyleList(styles)