Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/xlwt/Style.py: 38%

291 statements  

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

1from __future__ import print_function 

2# -*- coding: windows-1252 -*- 

3 

4from . import Formatting 

5from .BIFFRecords import NumberFormatRecord, XFRecord, StyleRecord 

6from .compat import basestring, xrange 

7 

8FIRST_USER_DEFINED_NUM_FORMAT_IDX = 164 

9 

10class XFStyle(object): 

11 

12 def __init__(self): 

13 self.num_format_str = 'General' 

14 self.font = Formatting.Font() 

15 self.alignment = Formatting.Alignment() 

16 self.borders = Formatting.Borders() 

17 self.pattern = Formatting.Pattern() 

18 self.protection = Formatting.Protection() 

19 

20default_style = XFStyle() 

21 

22class StyleCollection(object): 

23 _std_num_fmt_list = [ 

24 'general', 

25 '0', 

26 '0.00', 

27 '#,##0', 

28 '#,##0.00', 

29 '"$"#,##0_);("$"#,##0)', 

30 '"$"#,##0_);[Red]("$"#,##0)', 

31 '"$"#,##0.00_);("$"#,##0.00)', 

32 '"$"#,##0.00_);[Red]("$"#,##0.00)', 

33 '0%', 

34 '0.00%', 

35 '0.00E+00', 

36 '# ?/?', 

37 '# ??/??', 

38 'M/D/YY', 

39 'D-MMM-YY', 

40 'D-MMM', 

41 'MMM-YY', 

42 'h:mm AM/PM', 

43 'h:mm:ss AM/PM', 

44 'h:mm', 

45 'h:mm:ss', 

46 'M/D/YY h:mm', 

47 '_(#,##0_);(#,##0)', 

48 '_(#,##0_);[Red](#,##0)', 

49 '_(#,##0.00_);(#,##0.00)', 

50 '_(#,##0.00_);[Red](#,##0.00)', 

51 '_("$"* #,##0_);_("$"* (#,##0);_("$"* "-"_);_(@_)', 

52 '_(* #,##0_);_(* (#,##0);_(* "-"_);_(@_)', 

53 '_("$"* #,##0.00_);_("$"* (#,##0.00);_("$"* "-"??_);_(@_)', 

54 '_(* #,##0.00_);_(* (#,##0.00);_(* "-"??_);_(@_)', 

55 'mm:ss', 

56 '[h]:mm:ss', 

57 'mm:ss.0', 

58 '##0.0E+0', 

59 '@' 

60 ] 

61 

62 def __init__(self, style_compression=0): 

63 self.style_compression = style_compression 

64 self.stats = [0, 0, 0, 0, 0, 0] 

65 self._font_id2x = {} 

66 self._font_x2id = {} 

67 self._font_val2x = {} 

68 

69 for x in (0, 1, 2, 3, 5): # The font with index 4 is omitted in all BIFF versions 

70 font = Formatting.Font() 

71 search_key = font._search_key() 

72 self._font_id2x[font] = x 

73 self._font_x2id[x] = font 

74 self._font_val2x[search_key] = x 

75 

76 self._xf_id2x = {} 

77 self._xf_x2id = {} 

78 self._xf_val2x = {} 

79 

80 self._num_formats = {} 

81 for fmtidx, fmtstr in zip(range(0, 23), StyleCollection._std_num_fmt_list[0:23]): 

82 self._num_formats[fmtstr] = fmtidx 

83 for fmtidx, fmtstr in zip(range(37, 50), StyleCollection._std_num_fmt_list[23:]): 

84 self._num_formats[fmtstr] = fmtidx 

85 

86 self.default_style = XFStyle() 

87 self._default_xf = self._add_style(self.default_style)[0] 

88 

89 def add(self, style): 

90 if style == None: 

91 return 0x10 

92 return self._add_style(style)[1] 

93 

94 def _add_style(self, style): 

95 num_format_str = style.num_format_str 

96 if num_format_str in self._num_formats: 

97 num_format_idx = self._num_formats[num_format_str] 

98 else: 

99 num_format_idx = ( 

100 FIRST_USER_DEFINED_NUM_FORMAT_IDX 

101 + len(self._num_formats) 

102 - len(StyleCollection._std_num_fmt_list) 

103 ) 

104 self._num_formats[num_format_str] = num_format_idx 

105 

106 font = style.font 

107 if font in self._font_id2x: 

108 font_idx = self._font_id2x[font] 

109 self.stats[0] += 1 

110 elif self.style_compression: 

111 search_key = font._search_key() 

112 font_idx = self._font_val2x.get(search_key) 

113 if font_idx is not None: 

114 self._font_id2x[font] = font_idx 

115 self.stats[1] += 1 

116 else: 

117 font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing 

118 self._font_id2x[font] = font_idx 

119 self._font_val2x[search_key] = font_idx 

120 self._font_x2id[font_idx] = font 

121 self.stats[2] += 1 

122 else: 

123 font_idx = len(self._font_id2x) + 1 

124 self._font_id2x[font] = font_idx 

125 self.stats[2] += 1 

126 

127 gof = (style.alignment, style.borders, style.pattern, style.protection) 

128 xf = (font_idx, num_format_idx) + gof 

129 if xf in self._xf_id2x: 

130 xf_index = self._xf_id2x[xf] 

131 self.stats[3] += 1 

132 elif self.style_compression == 2: 

133 xf_key = (font_idx, num_format_idx) + tuple(obj._search_key() for obj in gof) 

134 xf_index = self._xf_val2x.get(xf_key) 

135 if xf_index is not None: 

136 self._xf_id2x[xf] = xf_index 

137 self.stats[4] += 1 

138 else: 

139 xf_index = 0x10 + len(self._xf_x2id) 

140 self._xf_id2x[xf] = xf_index 

141 self._xf_val2x[xf_key] = xf_index 

142 self._xf_x2id[xf_index] = xf 

143 self.stats[5] += 1 

144 else: 

145 xf_index = 0x10 + len(self._xf_id2x) 

146 self._xf_id2x[xf] = xf_index 

147 self.stats[5] += 1 

148 

149 if xf_index >= 0xFFF: 

150 # 12 bits allowed, 0xFFF is a sentinel value 

151 raise ValueError("More than 4094 XFs (styles)") 

152 

153 return xf, xf_index 

154 

155 def add_font(self, font): 

156 return self._add_font(font) 

157 

158 def _add_font(self, font): 

159 if font in self._font_id2x: 

160 font_idx = self._font_id2x[font] 

161 self.stats[0] += 1 

162 elif self.style_compression: 

163 search_key = font._search_key() 

164 font_idx = self._font_val2x.get(search_key) 

165 if font_idx is not None: 

166 self._font_id2x[font] = font_idx 

167 self.stats[1] += 1 

168 else: 

169 font_idx = len(self._font_x2id) + 1 # Why plus 1? Font 4 is missing 

170 self._font_id2x[font] = font_idx 

171 self._font_val2x[search_key] = font_idx 

172 self._font_x2id[font_idx] = font 

173 self.stats[2] += 1 

174 else: 

175 font_idx = len(self._font_id2x) + 1 

176 self._font_id2x[font] = font_idx 

177 self.stats[2] += 1 

178 

179 return font_idx 

180 

181 

182 def get_biff_data(self): 

183 result = b'' 

184 result += self._all_fonts() 

185 result += self._all_num_formats() 

186 result += self._all_cell_styles() 

187 result += self._all_styles() 

188 return result 

189 

190 def _all_fonts(self): 

191 result = b'' 

192 if self.style_compression: 

193 fonts = self._font_x2id.items() 

194 else: 

195 fonts = [(x, o) for o, x in self._font_id2x.items()] 

196 for font_idx, font in sorted(fonts): 

197 result += font.get_biff_record().get() 

198 return result 

199 

200 def _all_num_formats(self): 

201 result = b'' 

202 alist = [ 

203 (v, k) 

204 for k, v in self._num_formats.items() 

205 if v >= FIRST_USER_DEFINED_NUM_FORMAT_IDX 

206 ] 

207 alist.sort() 

208 for fmtidx, fmtstr in alist: 

209 result += NumberFormatRecord(fmtidx, fmtstr).get() 

210 return result 

211 

212 def _all_cell_styles(self): 

213 result = b'' 

214 for i in range(0, 16): 

215 result += XFRecord(self._default_xf, 'style').get() 

216 if self.style_compression == 2: 

217 styles = self._xf_x2id.items() 

218 else: 

219 styles = [(x, o) for o, x in self._xf_id2x.items()] 

220 for xf_idx, xf in sorted(styles): 

221 result += XFRecord(xf).get() 

222 return result 

223 

224 def _all_styles(self): 

225 return StyleRecord().get() 

226 

227# easyxf and its supporting objects ################################### 

228 

229class EasyXFException(Exception): 

230 pass 

231 

232class EasyXFCallerError(EasyXFException): 

233 pass 

234 

235class EasyXFAuthorError(EasyXFException): 

236 pass 

237 

238class IntULim(object): 

239 # If astring represents a valid unsigned integer ('123', '0xabcd', etc) 

240 # and it is <= limit, return the int value; otherwise return None. 

241 

242 def __init__(self, limit): 

243 self.limit = limit 

244 

245 def __call__(self, astring): 

246 try: 

247 value = int(astring, 0) 

248 except ValueError: 

249 return None 

250 if not 0 <= value <= self.limit: 

251 return None 

252 return value 

253 

254bool_map = { 

255 # Text values for all Boolean attributes 

256 '1': 1, 'yes': 1, 'true': 1, 'on': 1, 

257 '0': 0, 'no': 0, 'false': 0, 'off': 0, 

258 } 

259 

260border_line_map = { 

261 # Text values for these borders attributes: 

262 # left, right, top, bottom and diag 

263 'no_line': 0x00, 

264 'thin': 0x01, 

265 'medium': 0x02, 

266 'dashed': 0x03, 

267 'dotted': 0x04, 

268 'thick': 0x05, 

269 'double': 0x06, 

270 'hair': 0x07, 

271 'medium_dashed': 0x08, 

272 'thin_dash_dotted': 0x09, 

273 'medium_dash_dotted': 0x0a, 

274 'thin_dash_dot_dotted': 0x0b, 

275 'medium_dash_dot_dotted': 0x0c, 

276 'slanted_medium_dash_dotted': 0x0d, 

277 } 

278 

279charset_map = { 

280 # Text values for font.charset 

281 'ansi_latin': 0x00, 

282 'sys_default': 0x01, 

283 'symbol': 0x02, 

284 'apple_roman': 0x4d, 

285 'ansi_jap_shift_jis': 0x80, 

286 'ansi_kor_hangul': 0x81, 

287 'ansi_kor_johab': 0x82, 

288 'ansi_chinese_gbk': 0x86, 

289 'ansi_chinese_big5': 0x88, 

290 'ansi_greek': 0xa1, 

291 'ansi_turkish': 0xa2, 

292 'ansi_vietnamese': 0xa3, 

293 'ansi_hebrew': 0xb1, 

294 'ansi_arabic': 0xb2, 

295 'ansi_baltic': 0xba, 

296 'ansi_cyrillic': 0xcc, 

297 'ansi_thai': 0xde, 

298 'ansi_latin_ii': 0xee, 

299 'oem_latin_i': 0xff, 

300 } 

301 

302 

303# Text values for colour indices. "grey" is a synonym of "gray". 

304# The names are those given by Microsoft Excel 2003 to the colours 

305# in the default palette. There is no great correspondence with 

306# any W3C name-to-RGB mapping. 

307_colour_map_text = """\ 

308aqua 0x31 

309black 0x08 

310blue 0x0C 

311blue_gray 0x36 

312bright_green 0x0B 

313brown 0x3C 

314coral 0x1D 

315cyan_ega 0x0F 

316dark_blue 0x12 

317dark_blue_ega 0x12 

318dark_green 0x3A 

319dark_green_ega 0x11 

320dark_purple 0x1C 

321dark_red 0x10 

322dark_red_ega 0x10 

323dark_teal 0x38 

324dark_yellow 0x13 

325gold 0x33 

326gray_ega 0x17 

327gray25 0x16 

328gray40 0x37 

329gray50 0x17 

330gray80 0x3F 

331green 0x11 

332ice_blue 0x1F 

333indigo 0x3E 

334ivory 0x1A 

335lavender 0x2E 

336light_blue 0x30 

337light_green 0x2A 

338light_orange 0x34 

339light_turquoise 0x29 

340light_yellow 0x2B 

341lime 0x32 

342magenta_ega 0x0E 

343ocean_blue 0x1E 

344olive_ega 0x13 

345olive_green 0x3B 

346orange 0x35 

347pale_blue 0x2C 

348periwinkle 0x18 

349pink 0x0E 

350plum 0x3D 

351purple_ega 0x14 

352red 0x0A 

353rose 0x2D 

354sea_green 0x39 

355silver_ega 0x16 

356sky_blue 0x28 

357tan 0x2F 

358teal 0x15 

359teal_ega 0x15 

360turquoise 0x0F 

361violet 0x14 

362white 0x09 

363yellow 0x0D""" 

364 

365colour_map = {} 

366for _line in _colour_map_text.splitlines(): 

367 _name, _num = _line.split() 

368 _num = int(_num, 0) 

369 colour_map[_name] = _num 

370 if 'gray' in _name: 

371 colour_map[_name.replace('gray', 'grey')] = _num 

372del _colour_map_text, _line, _name, _num 

373 

374def add_palette_colour(colour_str, colour_index): 

375 if not (8 <= colour_index <= 63): 

376 raise Exception("add_palette_colour: colour_index (%d) not in range(8, 64)" % 

377 (colour_index)) 

378 colour_map[colour_str] = colour_index 

379 

380# user-defined palette defines 56 RGB colors from entry 8 - 64 

381#excel_default_palette_b8 = [ # (red, green, blue) 

382# ( 0, 0, 0), (255,255,255), (255, 0, 0), ( 0,255, 0), 

383# ( 0, 0,255), (255,255, 0), (255, 0,255), ( 0,255,255), 

384# (128, 0, 0), ( 0,128, 0), ( 0, 0,128), (128,128, 0), 

385# (128, 0,128), ( 0,128,128), (192,192,192), (128,128,128), 

386# (153,153,255), (153, 51,102), (255,255,204), (204,255,255), 

387# (102, 0,102), (255,128,128), ( 0,102,204), (204,204,255), 

388# ( 0, 0,128), (255, 0,255), (255,255, 0), ( 0,255,255), 

389# (128, 0,128), (128, 0, 0), ( 0,128,128), ( 0, 0,255), 

390# ( 0,204,255), (204,255,255), (204,255,204), (255,255,153), 

391# (153,204,255), (255,153,204), (204,153,255), (255,204,153), 

392# ( 51,102,255), ( 51,204,204), (153,204, 0), (255,204, 0), 

393# (255,153, 0), (255,102, 0), (102,102,153), (150,150,150), 

394# ( 0, 51,102), ( 51,153,102), ( 0, 51, 0), ( 51, 51, 0), 

395# (153, 51, 0), (153, 51,102), ( 51, 51,153), ( 51, 51, 51), 

396# ] 

397 

398# Default colour table for BIFF8 copied from  

399# OpenOffice.org's Documentation of the Microsoft Excel File Format, Excel Version 2003 

400# Note palette has LSB padded with 2 bytes 0x00 

401excel_default_palette_b8 = ( 

4020x00000000, 

4030xFFFFFF00, 

4040xFF000000, 

4050x00FF0000, 

4060x0000FF00, 

4070xFFFF0000, 

4080xFF00FF00, 

4090x00FFFF00, 

4100x80000000, 

4110x00800000, 

4120x00008000, 

4130x80800000, 

4140x80008000, 

4150x00808000, 

4160xC0C0C000, 

4170x80808000, 

4180x9999FF00, 

4190x99336600, 

4200xFFFFCC00, 

4210xCCFFFF00, 

4220x66006600, 

4230xFF808000, 

4240x0066CC00, 

4250xCCCCFF00, 

4260x00008000, 

4270xFF00FF00, 

4280xFFFF0000, 

4290x00FFFF00, 

4300x80008000, 

4310x80000000, 

4320x00808000, 

4330x0000FF00, 

4340x00CCFF00, 

4350xCCFFFF00, 

4360xCCFFCC00, 

4370xFFFF9900, 

4380x99CCFF00, 

4390xFF99CC00, 

4400xCC99FF00, 

4410xFFCC9900, 

4420x3366FF00, 

4430x33CCCC00, 

4440x99CC0000, 

4450xFFCC0000, 

4460xFF990000, 

4470xFF660000, 

4480x66669900, 

4490x96969600, 

4500x00336600, 

4510x33996600, 

4520x00330000, 

4530x33330000, 

4540x99330000, 

4550x99336600, 

4560x33339900, 

4570x33333300) 

458 

459assert len(excel_default_palette_b8) == 56 

460 

461pattern_map = { 

462 # Text values for pattern.pattern 

463 # xlwt/doc/pattern_examples.xls showcases all of these patterns. 

464 'no_fill': 0, 

465 'none': 0, 

466 'solid': 1, 

467 'solid_fill': 1, 

468 'solid_pattern': 1, 

469 'fine_dots': 2, 

470 'alt_bars': 3, 

471 'sparse_dots': 4, 

472 'thick_horz_bands': 5, 

473 'thick_vert_bands': 6, 

474 'thick_backward_diag': 7, 

475 'thick_forward_diag': 8, 

476 'big_spots': 9, 

477 'bricks': 10, 

478 'thin_horz_bands': 11, 

479 'thin_vert_bands': 12, 

480 'thin_backward_diag': 13, 

481 'thin_forward_diag': 14, 

482 'squares': 15, 

483 'diamonds': 16, 

484 } 

485 

486def any_str_func(s): 

487 return s.strip() 

488 

489def colour_index_func(s, maxval=0x7F): 

490 try: 

491 value = int(s, 0) 

492 except ValueError: 

493 return None 

494 if not (0 <= value <= maxval): 

495 return None 

496 return value 

497 

498colour_index_func_7 = colour_index_func 

499 

500def colour_index_func_15(s): 

501 return colour_index_func(s, maxval=0x7FFF) 

502 

503def rotation_func(s): 

504 try: 

505 value = int(s, 0) 

506 except ValueError: 

507 return None 

508 if not (-90 <= value <= 90): 

509 raise EasyXFCallerError("rotation %d: should be -90 to +90 degrees" % value) 

510 if value < 0: 

511 value = 90 - value # encode as 91 to 180 (clockwise) 

512 return value 

513 

514xf_dict = { 

515 'align': 'alignment', # synonym 

516 'alignment': { 

517 'dire': { 

518 'general': 0, 

519 'lr': 1, 

520 'rl': 2, 

521 }, 

522 'direction': 'dire', 

523 'horiz': 'horz', 

524 'horizontal': 'horz', 

525 'horz': { 

526 'general': 0, 

527 'left': 1, 

528 'center': 2, 

529 'centre': 2, # "align: horiz centre" means xf.alignment.horz is set to 2 

530 'right': 3, 

531 'filled': 4, 

532 'justified': 5, 

533 'center_across_selection': 6, 

534 'centre_across_selection': 6, 

535 'distributed': 7, 

536 }, 

537 'inde': IntULim(15), # restriction: 0 <= value <= 15 

538 'indent': 'inde', 

539 'rota': [{'stacked': 255, 'none': 0, }, rotation_func], 

540 'rotation': 'rota', 

541 'shri': bool_map, 

542 'shrink': 'shri', 

543 'shrink_to_fit': 'shri', 

544 'vert': { 

545 'top': 0, 

546 'center': 1, 

547 'centre': 1, 

548 'bottom': 2, 

549 'justified': 3, 

550 'distributed': 4, 

551 }, 

552 'vertical': 'vert', 

553 'wrap': bool_map, 

554 }, 

555 'border': 'borders', 

556 'borders': { 

557 'left': [border_line_map, IntULim(0x0d)], 

558 'right': [border_line_map, IntULim(0x0d)], 

559 'top': [border_line_map, IntULim(0x0d)], 

560 'bottom': [border_line_map, IntULim(0x0d)], 

561 'diag': [border_line_map, IntULim(0x0d)], 

562 'top_colour': [colour_map, colour_index_func_7], 

563 'bottom_colour': [colour_map, colour_index_func_7], 

564 'left_colour': [colour_map, colour_index_func_7], 

565 'right_colour': [colour_map, colour_index_func_7], 

566 'diag_colour': [colour_map, colour_index_func_7], 

567 'top_color': 'top_colour', 

568 'bottom_color': 'bottom_colour', 

569 'left_color': 'left_colour', 

570 'right_color': 'right_colour', 

571 'diag_color': 'diag_colour', 

572 'need_diag1': bool_map, 

573 'need_diag2': bool_map, 

574 }, 

575 'font': { 

576 'bold': bool_map, 

577 'charset': charset_map, 

578 'color': 'colour_index', 

579 'color_index': 'colour_index', 

580 'colour': 'colour_index', 

581 'colour_index': [colour_map, colour_index_func_15], 

582 'escapement': {'none': 0, 'superscript': 1, 'subscript': 2}, 

583 'family': {'none': 0, 'roman': 1, 'swiss': 2, 'modern': 3, 'script': 4, 'decorative': 5, }, 

584 'height': IntULim(0xFFFF), # practical limits are much narrower e.g. 160 to 1440 (8pt to 72pt) 

585 'italic': bool_map, 

586 'name': any_str_func, 

587 'outline': bool_map, 

588 'shadow': bool_map, 

589 'struck_out': bool_map, 

590 'underline': [bool_map, {'none': 0, 'single': 1, 'single_acc': 0x21, 'double': 2, 'double_acc': 0x22, }], 

591 }, 

592 'pattern': { 

593 'back_color': 'pattern_back_colour', 

594 'back_colour': 'pattern_back_colour', 

595 'fore_color': 'pattern_fore_colour', 

596 'fore_colour': 'pattern_fore_colour', 

597 'pattern': [pattern_map, IntULim(16)], 

598 'pattern_back_color': 'pattern_back_colour', 

599 'pattern_back_colour': [colour_map, colour_index_func_7], 

600 'pattern_fore_color': 'pattern_fore_colour', 

601 'pattern_fore_colour': [colour_map, colour_index_func_7], 

602 }, 

603 'protection': { 

604 'cell_locked' : bool_map, 

605 'formula_hidden': bool_map, 

606 }, 

607 } 

608 

609def _esplit(s, split_char, esc_char="\\"): 

610 escaped = False 

611 olist = [''] 

612 for c in s: 

613 if escaped: 613 ↛ 614line 613 didn't jump to line 614, because the condition on line 613 was never true

614 olist[-1] += c 

615 escaped = False 

616 elif c == esc_char: 616 ↛ 617line 616 didn't jump to line 617, because the condition on line 616 was never true

617 escaped = True 

618 elif c == split_char: 

619 olist.append('') 

620 else: 

621 olist[-1] += c 

622 return olist 

623 

624def _parse_strg_to_obj(strg, obj, parse_dict, 

625 field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False): 

626 for line in _esplit(strg, line_sep, esc_char): 

627 line = line.strip() 

628 if not line: 628 ↛ 629line 628 didn't jump to line 629, because the condition on line 628 was never true

629 break 

630 split_line = _esplit(line, intro_sep, esc_char) 

631 if len(split_line) != 2: 631 ↛ 632line 631 didn't jump to line 632, because the condition on line 631 was never true

632 raise EasyXFCallerError('line %r should have exactly 1 "%c"' % (line, intro_sep)) 

633 section, item_str = split_line 

634 section = section.strip().lower() 

635 for counter in range(2): 635 ↛ 648line 635 didn't jump to line 648, because the loop on line 635 didn't complete

636 result = parse_dict.get(section) 

637 if result is None: 637 ↛ 638line 637 didn't jump to line 638, because the condition on line 637 was never true

638 raise EasyXFCallerError('section %r is unknown' % section) 

639 if isinstance(result, dict): 639 ↛ 641line 639 didn't jump to line 641, because the condition on line 639 was never false

640 break 

641 if not isinstance(result, str): 

642 raise EasyXFAuthorError( 

643 'section %r should map to dict or str object; found %r' % (section, type(result))) 

644 # synonym 

645 old_section = section 

646 section = result 

647 else: 

648 raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_section, result)) 

649 section_dict = result 

650 section_obj = getattr(obj, section, None) 

651 if section_obj is None: 651 ↛ 652line 651 didn't jump to line 652, because the condition on line 651 was never true

652 raise EasyXFAuthorError('instance of %s class has no attribute named %s' % (obj.__class__.__name__, section)) 

653 for kv_str in _esplit(item_str, field_sep, esc_char): 

654 guff = kv_str.split() 

655 if not guff: 655 ↛ 656line 655 didn't jump to line 656, because the condition on line 655 was never true

656 continue 

657 k = guff[0].lower().replace('-', '_') 

658 v = ' '.join(guff[1:]) 

659 if not v: 659 ↛ 660line 659 didn't jump to line 660, because the condition on line 659 was never true

660 raise EasyXFCallerError("no value supplied for %s.%s" % (section, k)) 

661 for counter in xrange(2): 661 ↛ 671line 661 didn't jump to line 671, because the loop on line 661 didn't complete

662 result = section_dict.get(k) 

663 if result is None: 663 ↛ 664line 663 didn't jump to line 664, because the condition on line 663 was never true

664 raise EasyXFCallerError('%s.%s is not a known attribute' % (section, k)) 

665 if not isinstance(result, basestring): 665 ↛ 668line 665 didn't jump to line 668, because the condition on line 665 was never false

666 break 

667 # synonym 

668 old_k = k 

669 k = result 

670 else: 

671 raise EasyXFAuthorError('Attempt to define synonym of synonym (%r: %r)' % (old_k, result)) 

672 value_info = result 

673 if not isinstance(value_info, list): 673 ↛ 675line 673 didn't jump to line 675, because the condition on line 673 was never false

674 value_info = [value_info] 

675 for value_rule in value_info: 675 ↛ 689line 675 didn't jump to line 689, because the loop on line 675 didn't complete

676 if isinstance(value_rule, dict): 676 ↛ 682line 676 didn't jump to line 682, because the condition on line 676 was never false

677 # dict maps strings to integer field values 

678 vl = v.lower().replace('-', '_') 

679 if vl in value_rule: 679 ↛ 675line 679 didn't jump to line 675, because the condition on line 679 was never false

680 value = value_rule[vl] 

681 break 

682 elif callable(value_rule): 

683 value = value_rule(v) 

684 if value is not None: 

685 break 

686 else: 

687 raise EasyXFAuthorError("unknown value rule for attribute %r: %r" % (k, value_rule)) 

688 else: 

689 raise EasyXFCallerError("unexpected value %r for %s.%s" % (v, section, k)) 

690 try: 

691 orig = getattr(section_obj, k) 

692 except AttributeError: 

693 raise EasyXFAuthorError('%s.%s in dictionary but not in supplied object' % (section, k)) 

694 if debug: print("+++ %s.%s = %r # %s; was %r" % (section, k, value, v, orig)) 

695 setattr(section_obj, k, value) 

696 

697def easyxf(strg_to_parse="", num_format_str=None, 

698 field_sep=",", line_sep=";", intro_sep=":", esc_char="\\", debug=False): 

699 """ 

700 This function is used to create and configure 

701 :class:`XFStyle` objects for use with (for example) the 

702 :meth:`Worksheet.write` method. 

703 

704 It takes a string to be parsed to obtain attribute values for 

705 :class:`Alignment`, :class:`Borders`, :class:`Font`, :class:`Pattern` and 

706 :class:`Protection` objects. 

707 

708 Refer to the examples in the file `examples/xlwt_easyxf_simple_demo.py` 

709 and to the `xf_dict` dictionary in :mod:`xlwt.Style`. 

710 

711 Various synonyms including color/colour, center/centre and gray/grey are 

712 allowed. Case is irrelevant (except maybe in font names). ``-`` may be used 

713 instead of ``_``. 

714 

715 Example: ``font: bold on; align: wrap on, vert centre, horiz center`` 

716 

717 :param num_format_str: 

718 

719 To get the "number format string" of an existing 

720 cell whose format you want to reproduce, select the cell and click on 

721 Format/Cells/Number/Custom. Otherwise, refer to Excel help. 

722 

723 Examples: ``"#,##0.00"``, ``"dd/mm/yyyy"`` 

724 

725 :return: An :class:`XFstyle` object. 

726 

727 """ 

728 xfobj = XFStyle() 

729 if num_format_str is not None: 729 ↛ 730line 729 didn't jump to line 730, because the condition on line 729 was never true

730 xfobj.num_format_str = num_format_str 

731 if strg_to_parse: 731 ↛ 734line 731 didn't jump to line 734, because the condition on line 731 was never false

732 _parse_strg_to_obj(strg_to_parse, xfobj, xf_dict, 

733 field_sep=field_sep, line_sep=line_sep, intro_sep=intro_sep, esc_char=esc_char, debug=debug) 

734 return xfobj 

735 

736def easyfont(strg_to_parse="", field_sep=",", esc_char="\\", debug=False): 

737 xfobj = XFStyle() 

738 if strg_to_parse: 

739 _parse_strg_to_obj("font: " + strg_to_parse, xfobj, xf_dict, 

740 field_sep=field_sep, line_sep=";", intro_sep=":", esc_char=esc_char, debug=debug) 

741 return xfobj.font