Coverage for /usr/lib/python3/dist-packages/matplotlib/rcsetup.py: 69%

414 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2The rcsetup module contains the validation code for customization using 

3Matplotlib's rc settings. 

4 

5Each rc setting is assigned a function used to validate any attempted changes 

6to that setting. The validation functions are defined in the rcsetup module, 

7and are used to construct the rcParams global object which stores the settings 

8and is referenced throughout Matplotlib. 

9 

10The default values of the rc settings are set in the default matplotlibrc file. 

11Any additions or deletions to the parameter set listed here should also be 

12propagated to the :file:`matplotlibrc.template` in Matplotlib's root source 

13directory. 

14""" 

15 

16import ast 

17from functools import lru_cache, reduce 

18from numbers import Number 

19import operator 

20import os 

21import re 

22 

23import numpy as np 

24 

25from matplotlib import _api, cbook 

26from matplotlib.cbook import ls_mapper 

27from matplotlib.colors import Colormap, is_color_like 

28from matplotlib._fontconfig_pattern import parse_fontconfig_pattern 

29from matplotlib._enums import JoinStyle, CapStyle 

30 

31# Don't let the original cycler collide with our validating cycler 

32from cycler import Cycler, cycler as ccycler 

33 

34 

35# The capitalized forms are needed for ipython at present; this may 

36# change for later versions. 

37interactive_bk = [ 

38 'GTK3Agg', 'GTK3Cairo', 'GTK4Agg', 'GTK4Cairo', 

39 'MacOSX', 

40 'nbAgg', 

41 'QtAgg', 'QtCairo', 'Qt5Agg', 'Qt5Cairo', 

42 'TkAgg', 'TkCairo', 

43 'WebAgg', 

44 'WX', 'WXAgg', 'WXCairo', 

45] 

46non_interactive_bk = ['agg', 'cairo', 

47 'pdf', 'pgf', 'ps', 'svg', 'template'] 

48all_backends = interactive_bk + non_interactive_bk 

49 

50 

51class ValidateInStrings: 

52 def __init__(self, key, valid, ignorecase=False, *, 

53 _deprecated_since=None): 

54 """*valid* is a list of legal strings.""" 

55 self.key = key 

56 self.ignorecase = ignorecase 

57 self._deprecated_since = _deprecated_since 

58 

59 def func(s): 

60 if ignorecase: 

61 return s.lower() 

62 else: 

63 return s 

64 self.valid = {func(k): k for k in valid} 

65 

66 def __call__(self, s): 

67 if self._deprecated_since: 

68 name, = (k for k, v in globals().items() if v is self) 

69 _api.warn_deprecated( 

70 self._deprecated_since, name=name, obj_type="function") 

71 if self.ignorecase and isinstance(s, str): 

72 s = s.lower() 

73 if s in self.valid: 

74 return self.valid[s] 

75 msg = (f"{s!r} is not a valid value for {self.key}; supported values " 

76 f"are {[*self.valid.values()]}") 

77 if (isinstance(s, str) 

78 and (s.startswith('"') and s.endswith('"') 

79 or s.startswith("'") and s.endswith("'")) 

80 and s[1:-1] in self.valid): 

81 msg += "; remove quotes surrounding your string" 

82 raise ValueError(msg) 

83 

84 

85@lru_cache() 

86def _listify_validator(scalar_validator, allow_stringlist=False, *, 

87 n=None, doc=None): 

88 def f(s): 

89 if isinstance(s, str): 

90 try: 

91 val = [scalar_validator(v.strip()) for v in s.split(',') 

92 if v.strip()] 

93 except Exception: 

94 if allow_stringlist: 

95 # Sometimes, a list of colors might be a single string 

96 # of single-letter colornames. So give that a shot. 

97 val = [scalar_validator(v.strip()) for v in s if v.strip()] 

98 else: 

99 raise 

100 # Allow any ordered sequence type -- generators, np.ndarray, pd.Series 

101 # -- but not sets, whose iteration order is non-deterministic. 

102 elif np.iterable(s) and not isinstance(s, (set, frozenset)): 

103 # The condition on this list comprehension will preserve the 

104 # behavior of filtering out any empty strings (behavior was 

105 # from the original validate_stringlist()), while allowing 

106 # any non-string/text scalar values such as numbers and arrays. 

107 val = [scalar_validator(v) for v in s 

108 if not isinstance(v, str) or v] 

109 else: 

110 raise ValueError( 

111 f"Expected str or other non-set iterable, but got {s}") 

112 if n is not None and len(val) != n: 

113 raise ValueError( 

114 f"Expected {n} values, but there are {len(val)} values in {s}") 

115 return val 

116 

117 try: 

118 f.__name__ = "{}list".format(scalar_validator.__name__) 

119 except AttributeError: # class instance. 

120 f.__name__ = "{}List".format(type(scalar_validator).__name__) 

121 f.__qualname__ = f.__qualname__.rsplit(".", 1)[0] + "." + f.__name__ 

122 f.__doc__ = doc if doc is not None else scalar_validator.__doc__ 

123 return f 

124 

125 

126def validate_any(s): 

127 return s 

128validate_anylist = _listify_validator(validate_any) 

129 

130 

131def _validate_date(s): 

132 try: 

133 np.datetime64(s) 

134 return s 

135 except ValueError: 

136 raise ValueError( 

137 f'{s!r} should be a string that can be parsed by numpy.datetime64') 

138 

139 

140def validate_bool(b): 

141 """Convert b to ``bool`` or raise.""" 

142 if isinstance(b, str): 

143 b = b.lower() 

144 if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True): 

145 return True 

146 elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False): 

147 return False 

148 else: 

149 raise ValueError(f'Cannot convert {b!r} to bool') 

150 

151 

152def validate_axisbelow(s): 

153 try: 

154 return validate_bool(s) 

155 except ValueError: 

156 if isinstance(s, str): 

157 if s == 'line': 

158 return 'line' 

159 raise ValueError(f'{s!r} cannot be interpreted as' 

160 ' True, False, or "line"') 

161 

162 

163def validate_dpi(s): 

164 """Confirm s is string 'figure' or convert s to float or raise.""" 

165 if s == 'figure': 

166 return s 

167 try: 

168 return float(s) 

169 except ValueError as e: 

170 raise ValueError(f'{s!r} is not string "figure" and ' 

171 f'could not convert {s!r} to float') from e 

172 

173 

174def _make_type_validator(cls, *, allow_none=False): 

175 """ 

176 Return a validator that converts inputs to *cls* or raises (and possibly 

177 allows ``None`` as well). 

178 """ 

179 

180 def validator(s): 

181 if (allow_none and 

182 (s is None or isinstance(s, str) and s.lower() == "none")): 

183 return None 

184 if cls is str and not isinstance(s, str): 

185 _api.warn_deprecated( 

186 "3.5", message="Support for setting an rcParam that expects a " 

187 "str value to a non-str value is deprecated since %(since)s " 

188 "and support will be removed %(removal)s.") 

189 try: 

190 return cls(s) 

191 except (TypeError, ValueError) as e: 

192 raise ValueError( 

193 f'Could not convert {s!r} to {cls.__name__}') from e 

194 

195 validator.__name__ = f"validate_{cls.__name__}" 

196 if allow_none: 

197 validator.__name__ += "_or_None" 

198 validator.__qualname__ = ( 

199 validator.__qualname__.rsplit(".", 1)[0] + "." + validator.__name__) 

200 return validator 

201 

202 

203validate_string = _make_type_validator(str) 

204validate_string_or_None = _make_type_validator(str, allow_none=True) 

205validate_stringlist = _listify_validator( 

206 validate_string, doc='return a list of strings') 

207validate_int = _make_type_validator(int) 

208validate_int_or_None = _make_type_validator(int, allow_none=True) 

209validate_float = _make_type_validator(float) 

210validate_float_or_None = _make_type_validator(float, allow_none=True) 

211validate_floatlist = _listify_validator( 

212 validate_float, doc='return a list of floats') 

213 

214 

215def _validate_pathlike(s): 

216 if isinstance(s, (str, os.PathLike)): 

217 # Store value as str because savefig.directory needs to distinguish 

218 # between "" (cwd) and "." (cwd, but gets updated by user selections). 

219 return os.fsdecode(s) 

220 else: 

221 return validate_string(s) # Emit deprecation warning. 

222 

223 

224def validate_fonttype(s): 

225 """ 

226 Confirm that this is a Postscript or PDF font type that we know how to 

227 convert to. 

228 """ 

229 fonttypes = {'type3': 3, 

230 'truetype': 42} 

231 try: 

232 fonttype = validate_int(s) 

233 except ValueError: 

234 try: 

235 return fonttypes[s.lower()] 

236 except KeyError as e: 

237 raise ValueError('Supported Postscript/PDF font types are %s' 

238 % list(fonttypes)) from e 

239 else: 

240 if fonttype not in fonttypes.values(): 

241 raise ValueError( 

242 'Supported Postscript/PDF font types are %s' % 

243 list(fonttypes.values())) 

244 return fonttype 

245 

246 

247_validate_standard_backends = ValidateInStrings( 

248 'backend', all_backends, ignorecase=True) 

249_auto_backend_sentinel = object() 

250 

251 

252def validate_backend(s): 

253 backend = ( 

254 s if s is _auto_backend_sentinel or s.startswith("module://") 

255 else _validate_standard_backends(s)) 

256 return backend 

257 

258 

259def _validate_toolbar(s): 

260 s = ValidateInStrings( 

261 'toolbar', ['None', 'toolbar2', 'toolmanager'], ignorecase=True)(s) 

262 if s == 'toolmanager': 

263 _api.warn_external( 

264 "Treat the new Tool classes introduced in v1.5 as experimental " 

265 "for now; the API and rcParam may change in future versions.") 

266 return s 

267 

268 

269def validate_color_or_inherit(s): 

270 """Return a valid color arg.""" 

271 if cbook._str_equal(s, 'inherit'): 

272 return s 

273 return validate_color(s) 

274 

275 

276def validate_color_or_auto(s): 

277 if cbook._str_equal(s, 'auto'): 

278 return s 

279 return validate_color(s) 

280 

281 

282def validate_color_for_prop_cycle(s): 

283 # N-th color cycle syntax can't go into the color cycle. 

284 if isinstance(s, str) and re.match("^C[0-9]$", s): 

285 raise ValueError(f"Cannot put cycle reference ({s!r}) in prop_cycler") 

286 return validate_color(s) 

287 

288 

289def _validate_color_or_linecolor(s): 

290 if cbook._str_equal(s, 'linecolor'): 

291 return s 

292 elif cbook._str_equal(s, 'mfc') or cbook._str_equal(s, 'markerfacecolor'): 

293 return 'markerfacecolor' 

294 elif cbook._str_equal(s, 'mec') or cbook._str_equal(s, 'markeredgecolor'): 

295 return 'markeredgecolor' 

296 elif s is None: 

297 return None 

298 elif isinstance(s, str) and len(s) == 6 or len(s) == 8: 

299 stmp = '#' + s 

300 if is_color_like(stmp): 

301 return stmp 

302 if s.lower() == 'none': 

303 return None 

304 elif is_color_like(s): 

305 return s 

306 

307 raise ValueError(f'{s!r} does not look like a color arg') 

308 

309 

310def validate_color(s): 

311 """Return a valid color arg.""" 

312 if isinstance(s, str): 

313 if s.lower() == 'none': 

314 return 'none' 

315 if len(s) == 6 or len(s) == 8: 

316 stmp = '#' + s 

317 if is_color_like(stmp): 

318 return stmp 

319 

320 if is_color_like(s): 

321 return s 

322 

323 # If it is still valid, it must be a tuple (as a string from matplotlibrc). 

324 try: 

325 color = ast.literal_eval(s) 

326 except (SyntaxError, ValueError): 

327 pass 

328 else: 

329 if is_color_like(color): 

330 return color 

331 

332 raise ValueError(f'{s!r} does not look like a color arg') 

333 

334 

335validate_colorlist = _listify_validator( 

336 validate_color, allow_stringlist=True, doc='return a list of colorspecs') 

337 

338 

339def _validate_cmap(s): 

340 _api.check_isinstance((str, Colormap), cmap=s) 

341 return s 

342 

343 

344def validate_aspect(s): 

345 if s in ('auto', 'equal'): 

346 return s 

347 try: 

348 return float(s) 

349 except ValueError as e: 

350 raise ValueError('not a valid aspect specification') from e 

351 

352 

353def validate_fontsize_None(s): 

354 if s is None or s == 'None': 

355 return None 

356 else: 

357 return validate_fontsize(s) 

358 

359 

360def validate_fontsize(s): 

361 fontsizes = ['xx-small', 'x-small', 'small', 'medium', 'large', 

362 'x-large', 'xx-large', 'smaller', 'larger'] 

363 if isinstance(s, str): 

364 s = s.lower() 

365 if s in fontsizes: 

366 return s 

367 try: 

368 return float(s) 

369 except ValueError as e: 

370 raise ValueError("%s is not a valid font size. Valid font sizes " 

371 "are %s." % (s, ", ".join(fontsizes))) from e 

372 

373 

374validate_fontsizelist = _listify_validator(validate_fontsize) 

375 

376 

377def validate_fontweight(s): 

378 weights = [ 

379 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman', 

380 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black'] 

381 # Note: Historically, weights have been case-sensitive in Matplotlib 

382 if s in weights: 

383 return s 

384 try: 

385 return int(s) 

386 except (ValueError, TypeError) as e: 

387 raise ValueError(f'{s} is not a valid font weight.') from e 

388 

389 

390def validate_fontstretch(s): 

391 stretchvalues = [ 

392 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', 

393 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 

394 'ultra-expanded'] 

395 # Note: Historically, stretchvalues have been case-sensitive in Matplotlib 

396 if s in stretchvalues: 

397 return s 

398 try: 

399 return int(s) 

400 except (ValueError, TypeError) as e: 

401 raise ValueError(f'{s} is not a valid font stretch.') from e 

402 

403 

404def validate_font_properties(s): 

405 parse_fontconfig_pattern(s) 

406 return s 

407 

408 

409def _validate_mathtext_fallback(s): 

410 _fallback_fonts = ['cm', 'stix', 'stixsans'] 

411 if isinstance(s, str): 

412 s = s.lower() 

413 if s is None or s == 'none': 

414 return None 

415 elif s.lower() in _fallback_fonts: 

416 return s 

417 else: 

418 raise ValueError( 

419 f"{s} is not a valid fallback font name. Valid fallback font " 

420 f"names are {','.join(_fallback_fonts)}. Passing 'None' will turn " 

421 "fallback off.") 

422 

423 

424def validate_whiskers(s): 

425 try: 

426 return _listify_validator(validate_float, n=2)(s) 

427 except (TypeError, ValueError): 

428 try: 

429 return float(s) 

430 except ValueError as e: 

431 raise ValueError("Not a valid whisker value [float, " 

432 "(float, float)]") from e 

433 

434 

435def validate_ps_distiller(s): 

436 if isinstance(s, str): 

437 s = s.lower() 

438 if s in ('none', None, 'false', False): 

439 return None 

440 else: 

441 return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s) 

442 

443 

444# A validator dedicated to the named line styles, based on the items in 

445# ls_mapper, and a list of possible strings read from Line2D.set_linestyle 

446_validate_named_linestyle = ValidateInStrings( 

447 'linestyle', 

448 [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''], 

449 ignorecase=True) 

450 

451 

452def _validate_linestyle(ls): 

453 """ 

454 A validator for all possible line styles, the named ones *and* 

455 the on-off ink sequences. 

456 """ 

457 if isinstance(ls, str): 

458 try: # Look first for a valid named line style, like '--' or 'solid'. 

459 return _validate_named_linestyle(ls) 

460 except ValueError: 

461 pass 

462 try: 

463 ls = ast.literal_eval(ls) # Parsing matplotlibrc. 

464 except (SyntaxError, ValueError): 

465 pass # Will error with the ValueError at the end. 

466 

467 def _is_iterable_not_string_like(x): 

468 # Explicitly exclude bytes/bytearrays so that they are not 

469 # nonsensically interpreted as sequences of numbers (codepoints). 

470 return np.iterable(x) and not isinstance(x, (str, bytes, bytearray)) 

471 

472 if _is_iterable_not_string_like(ls): 

473 if len(ls) == 2 and _is_iterable_not_string_like(ls[1]): 

474 # (offset, (on, off, on, off, ...)) 

475 offset, onoff = ls 

476 else: 

477 # For backcompat: (on, off, on, off, ...); the offset is implicit. 

478 offset = 0 

479 onoff = ls 

480 

481 if (isinstance(offset, Number) 

482 and len(onoff) % 2 == 0 

483 and all(isinstance(elem, Number) for elem in onoff)): 

484 return (offset, onoff) 

485 

486 raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") 

487 

488 

489validate_fillstyle = ValidateInStrings( 

490 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none']) 

491 

492 

493validate_fillstylelist = _listify_validator(validate_fillstyle) 

494 

495 

496def validate_markevery(s): 

497 """ 

498 Validate the markevery property of a Line2D object. 

499 

500 Parameters 

501 ---------- 

502 s : None, int, (int, int), slice, float, (float, float), or list[int] 

503 

504 Returns 

505 ------- 

506 None, int, (int, int), slice, float, (float, float), or list[int] 

507 """ 

508 # Validate s against type slice float int and None 

509 if isinstance(s, (slice, float, int, type(None))): 

510 return s 

511 # Validate s against type tuple 

512 if isinstance(s, tuple): 

513 if (len(s) == 2 

514 and (all(isinstance(e, int) for e in s) 

515 or all(isinstance(e, float) for e in s))): 

516 return s 

517 else: 

518 raise TypeError( 

519 "'markevery' tuple must be pair of ints or of floats") 

520 # Validate s against type list 

521 if isinstance(s, list): 

522 if all(isinstance(e, int) for e in s): 

523 return s 

524 else: 

525 raise TypeError( 

526 "'markevery' list must have all elements of type int") 

527 raise TypeError("'markevery' is of an invalid type") 

528 

529 

530validate_markeverylist = _listify_validator(validate_markevery) 

531 

532 

533def validate_bbox(s): 

534 if isinstance(s, str): 

535 s = s.lower() 

536 if s == 'tight': 

537 return s 

538 if s == 'standard': 

539 return None 

540 raise ValueError("bbox should be 'tight' or 'standard'") 

541 elif s is not None: 

542 # Backwards compatibility. None is equivalent to 'standard'. 

543 raise ValueError("bbox should be 'tight' or 'standard'") 

544 return s 

545 

546 

547def validate_sketch(s): 

548 if isinstance(s, str): 

549 s = s.lower() 

550 if s == 'none' or s is None: 

551 return None 

552 try: 

553 return tuple(_listify_validator(validate_float, n=3)(s)) 

554 except ValueError: 

555 raise ValueError("Expected a (scale, length, randomness) triplet") 

556 

557 

558def _validate_greaterequal0_lessthan1(s): 

559 s = validate_float(s) 

560 if 0 <= s < 1: 

561 return s 

562 else: 

563 raise RuntimeError(f'Value must be >=0 and <1; got {s}') 

564 

565 

566def _validate_greaterequal0_lessequal1(s): 

567 s = validate_float(s) 

568 if 0 <= s <= 1: 

569 return s 

570 else: 

571 raise RuntimeError(f'Value must be >=0 and <=1; got {s}') 

572 

573 

574_range_validators = { # Slightly nicer (internal) API. 

575 "0 <= x < 1": _validate_greaterequal0_lessthan1, 

576 "0 <= x <= 1": _validate_greaterequal0_lessequal1, 

577} 

578 

579 

580def validate_hatch(s): 

581 r""" 

582 Validate a hatch pattern. 

583 A hatch pattern string can have any sequence of the following 

584 characters: ``\ / | - + * . x o O``. 

585 """ 

586 if not isinstance(s, str): 

587 raise ValueError("Hatch pattern must be a string") 

588 _api.check_isinstance(str, hatch_pattern=s) 

589 unknown = set(s) - {'\\', '/', '|', '-', '+', '*', '.', 'x', 'o', 'O'} 

590 if unknown: 

591 raise ValueError("Unknown hatch symbol(s): %s" % list(unknown)) 

592 return s 

593 

594 

595validate_hatchlist = _listify_validator(validate_hatch) 

596validate_dashlist = _listify_validator(validate_floatlist) 

597 

598 

599_prop_validators = { 

600 'color': _listify_validator(validate_color_for_prop_cycle, 

601 allow_stringlist=True), 

602 'linewidth': validate_floatlist, 

603 'linestyle': _listify_validator(_validate_linestyle), 

604 'facecolor': validate_colorlist, 

605 'edgecolor': validate_colorlist, 

606 'joinstyle': _listify_validator(JoinStyle), 

607 'capstyle': _listify_validator(CapStyle), 

608 'fillstyle': validate_fillstylelist, 

609 'markerfacecolor': validate_colorlist, 

610 'markersize': validate_floatlist, 

611 'markeredgewidth': validate_floatlist, 

612 'markeredgecolor': validate_colorlist, 

613 'markevery': validate_markeverylist, 

614 'alpha': validate_floatlist, 

615 'marker': validate_stringlist, 

616 'hatch': validate_hatchlist, 

617 'dashes': validate_dashlist, 

618 } 

619_prop_aliases = { 

620 'c': 'color', 

621 'lw': 'linewidth', 

622 'ls': 'linestyle', 

623 'fc': 'facecolor', 

624 'ec': 'edgecolor', 

625 'mfc': 'markerfacecolor', 

626 'mec': 'markeredgecolor', 

627 'mew': 'markeredgewidth', 

628 'ms': 'markersize', 

629 } 

630 

631 

632def cycler(*args, **kwargs): 

633 """ 

634 Create a `~cycler.Cycler` object much like :func:`cycler.cycler`, 

635 but includes input validation. 

636 

637 Call signatures:: 

638 

639 cycler(cycler) 

640 cycler(label=values[, label2=values2[, ...]]) 

641 cycler(label, values) 

642 

643 Form 1 copies a given `~cycler.Cycler` object. 

644 

645 Form 2 creates a `~cycler.Cycler` which cycles over one or more 

646 properties simultaneously. If multiple properties are given, their 

647 value lists must have the same length. 

648 

649 Form 3 creates a `~cycler.Cycler` for a single property. This form 

650 exists for compatibility with the original cycler. Its use is 

651 discouraged in favor of the kwarg form, i.e. ``cycler(label=values)``. 

652 

653 Parameters 

654 ---------- 

655 cycler : Cycler 

656 Copy constructor for Cycler. 

657 

658 label : str 

659 The property key. Must be a valid `.Artist` property. 

660 For example, 'color' or 'linestyle'. Aliases are allowed, 

661 such as 'c' for 'color' and 'lw' for 'linewidth'. 

662 

663 values : iterable 

664 Finite-length iterable of the property values. These values 

665 are validated and will raise a ValueError if invalid. 

666 

667 Returns 

668 ------- 

669 Cycler 

670 A new :class:`~cycler.Cycler` for the given properties. 

671 

672 Examples 

673 -------- 

674 Creating a cycler for a single property: 

675 

676 >>> c = cycler(color=['red', 'green', 'blue']) 

677 

678 Creating a cycler for simultaneously cycling over multiple properties 

679 (e.g. red circle, green plus, blue cross): 

680 

681 >>> c = cycler(color=['red', 'green', 'blue'], 

682 ... marker=['o', '+', 'x']) 

683 

684 """ 

685 if args and kwargs: 

686 raise TypeError("cycler() can only accept positional OR keyword " 

687 "arguments -- not both.") 

688 elif not args and not kwargs: 

689 raise TypeError("cycler() must have positional OR keyword arguments") 

690 

691 if len(args) == 1: 

692 if not isinstance(args[0], Cycler): 

693 raise TypeError("If only one positional argument given, it must " 

694 "be a Cycler instance.") 

695 return validate_cycler(args[0]) 

696 elif len(args) == 2: 

697 pairs = [(args[0], args[1])] 

698 elif len(args) > 2: 

699 raise TypeError("No more than 2 positional arguments allowed") 

700 else: 

701 pairs = kwargs.items() 

702 

703 validated = [] 

704 for prop, vals in pairs: 

705 norm_prop = _prop_aliases.get(prop, prop) 

706 validator = _prop_validators.get(norm_prop, None) 

707 if validator is None: 

708 raise TypeError("Unknown artist property: %s" % prop) 

709 vals = validator(vals) 

710 # We will normalize the property names as well to reduce 

711 # the amount of alias handling code elsewhere. 

712 validated.append((norm_prop, vals)) 

713 

714 return reduce(operator.add, (ccycler(k, v) for k, v in validated)) 

715 

716 

717class _DunderChecker(ast.NodeVisitor): 

718 def visit_Attribute(self, node): 

719 if node.attr.startswith("__") and node.attr.endswith("__"): 

720 raise ValueError("cycler strings with dunders are forbidden") 

721 self.generic_visit(node) 

722 

723 

724def validate_cycler(s): 

725 """Return a Cycler object from a string repr or the object itself.""" 

726 if isinstance(s, str): 

727 # TODO: We might want to rethink this... 

728 # While I think I have it quite locked down, it is execution of 

729 # arbitrary code without sanitation. 

730 # Combine this with the possibility that rcparams might come from the 

731 # internet (future plans), this could be downright dangerous. 

732 # I locked it down by only having the 'cycler()' function available. 

733 # UPDATE: Partly plugging a security hole. 

734 # I really should have read this: 

735 # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html 

736 # We should replace this eval with a combo of PyParsing and 

737 # ast.literal_eval() 

738 try: 

739 _DunderChecker().visit(ast.parse(s)) 

740 s = eval(s, {'cycler': cycler, '__builtins__': {}}) 

741 except BaseException as e: 

742 raise ValueError(f"{s!r} is not a valid cycler construction: {e}" 

743 ) from e 

744 # Should make sure what comes from the above eval() 

745 # is a Cycler object. 

746 if isinstance(s, Cycler): 

747 cycler_inst = s 

748 else: 

749 raise ValueError(f"Object is not a string or Cycler instance: {s!r}") 

750 

751 unknowns = cycler_inst.keys - (set(_prop_validators) | set(_prop_aliases)) 

752 if unknowns: 

753 raise ValueError("Unknown artist properties: %s" % unknowns) 

754 

755 # Not a full validation, but it'll at least normalize property names 

756 # A fuller validation would require v0.10 of cycler. 

757 checker = set() 

758 for prop in cycler_inst.keys: 

759 norm_prop = _prop_aliases.get(prop, prop) 

760 if norm_prop != prop and norm_prop in cycler_inst.keys: 

761 raise ValueError(f"Cannot specify both {norm_prop!r} and alias " 

762 f"{prop!r} in the same prop_cycle") 

763 if norm_prop in checker: 

764 raise ValueError(f"Another property was already aliased to " 

765 f"{norm_prop!r}. Collision normalizing {prop!r}.") 

766 checker.update([norm_prop]) 

767 

768 # This is just an extra-careful check, just in case there is some 

769 # edge-case I haven't thought of. 

770 assert len(checker) == len(cycler_inst.keys) 

771 

772 # Now, it should be safe to mutate this cycler 

773 for prop in cycler_inst.keys: 

774 norm_prop = _prop_aliases.get(prop, prop) 

775 cycler_inst.change_key(prop, norm_prop) 

776 

777 for key, vals in cycler_inst.by_key().items(): 

778 _prop_validators[key](vals) 

779 

780 return cycler_inst 

781 

782 

783def validate_hist_bins(s): 

784 valid_strs = ["auto", "sturges", "fd", "doane", "scott", "rice", "sqrt"] 

785 if isinstance(s, str) and s in valid_strs: 

786 return s 

787 try: 

788 return int(s) 

789 except (TypeError, ValueError): 

790 pass 

791 try: 

792 return validate_floatlist(s) 

793 except ValueError: 

794 pass 

795 raise ValueError("'hist.bins' must be one of {}, an int or" 

796 " a sequence of floats".format(valid_strs)) 

797 

798 

799class _ignorecase(list): 

800 """A marker class indicating that a list-of-str is case-insensitive.""" 

801 

802 

803def _convert_validator_spec(key, conv): 

804 if isinstance(conv, list): 

805 ignorecase = isinstance(conv, _ignorecase) 

806 return ValidateInStrings(key, conv, ignorecase=ignorecase) 

807 else: 

808 return conv 

809 

810 

811# Mapping of rcParams to validators. 

812# Converters given as lists or _ignorecase are converted to ValidateInStrings 

813# immediately below. 

814# The rcParams defaults are defined in matplotlibrc.template, which gets copied 

815# to matplotlib/mpl-data/matplotlibrc by the setup script. 

816_validators = { 

817 "backend": validate_backend, 

818 "backend_fallback": validate_bool, 

819 "toolbar": _validate_toolbar, 

820 "interactive": validate_bool, 

821 "timezone": validate_string, 

822 

823 "webagg.port": validate_int, 

824 "webagg.address": validate_string, 

825 "webagg.open_in_browser": validate_bool, 

826 "webagg.port_retries": validate_int, 

827 

828 # line props 

829 "lines.linewidth": validate_float, # line width in points 

830 "lines.linestyle": _validate_linestyle, # solid line 

831 "lines.color": validate_color, # first color in color cycle 

832 "lines.marker": validate_string, # marker name 

833 "lines.markerfacecolor": validate_color_or_auto, # default color 

834 "lines.markeredgecolor": validate_color_or_auto, # default color 

835 "lines.markeredgewidth": validate_float, 

836 "lines.markersize": validate_float, # markersize, in points 

837 "lines.antialiased": validate_bool, # antialiased (no jaggies) 

838 "lines.dash_joinstyle": JoinStyle, 

839 "lines.solid_joinstyle": JoinStyle, 

840 "lines.dash_capstyle": CapStyle, 

841 "lines.solid_capstyle": CapStyle, 

842 "lines.dashed_pattern": validate_floatlist, 

843 "lines.dashdot_pattern": validate_floatlist, 

844 "lines.dotted_pattern": validate_floatlist, 

845 "lines.scale_dashes": validate_bool, 

846 

847 # marker props 

848 "markers.fillstyle": validate_fillstyle, 

849 

850 ## pcolor(mesh) props: 

851 "pcolor.shading": ["auto", "flat", "nearest", "gouraud"], 

852 "pcolormesh.snap": validate_bool, 

853 

854 ## patch props 

855 "patch.linewidth": validate_float, # line width in points 

856 "patch.edgecolor": validate_color, 

857 "patch.force_edgecolor": validate_bool, 

858 "patch.facecolor": validate_color, # first color in cycle 

859 "patch.antialiased": validate_bool, # antialiased (no jaggies) 

860 

861 ## hatch props 

862 "hatch.color": validate_color, 

863 "hatch.linewidth": validate_float, 

864 

865 ## Histogram properties 

866 "hist.bins": validate_hist_bins, 

867 

868 ## Boxplot properties 

869 "boxplot.notch": validate_bool, 

870 "boxplot.vertical": validate_bool, 

871 "boxplot.whiskers": validate_whiskers, 

872 "boxplot.bootstrap": validate_int_or_None, 

873 "boxplot.patchartist": validate_bool, 

874 "boxplot.showmeans": validate_bool, 

875 "boxplot.showcaps": validate_bool, 

876 "boxplot.showbox": validate_bool, 

877 "boxplot.showfliers": validate_bool, 

878 "boxplot.meanline": validate_bool, 

879 

880 "boxplot.flierprops.color": validate_color, 

881 "boxplot.flierprops.marker": validate_string, 

882 "boxplot.flierprops.markerfacecolor": validate_color_or_auto, 

883 "boxplot.flierprops.markeredgecolor": validate_color, 

884 "boxplot.flierprops.markeredgewidth": validate_float, 

885 "boxplot.flierprops.markersize": validate_float, 

886 "boxplot.flierprops.linestyle": _validate_linestyle, 

887 "boxplot.flierprops.linewidth": validate_float, 

888 

889 "boxplot.boxprops.color": validate_color, 

890 "boxplot.boxprops.linewidth": validate_float, 

891 "boxplot.boxprops.linestyle": _validate_linestyle, 

892 

893 "boxplot.whiskerprops.color": validate_color, 

894 "boxplot.whiskerprops.linewidth": validate_float, 

895 "boxplot.whiskerprops.linestyle": _validate_linestyle, 

896 

897 "boxplot.capprops.color": validate_color, 

898 "boxplot.capprops.linewidth": validate_float, 

899 "boxplot.capprops.linestyle": _validate_linestyle, 

900 

901 "boxplot.medianprops.color": validate_color, 

902 "boxplot.medianprops.linewidth": validate_float, 

903 "boxplot.medianprops.linestyle": _validate_linestyle, 

904 

905 "boxplot.meanprops.color": validate_color, 

906 "boxplot.meanprops.marker": validate_string, 

907 "boxplot.meanprops.markerfacecolor": validate_color, 

908 "boxplot.meanprops.markeredgecolor": validate_color, 

909 "boxplot.meanprops.markersize": validate_float, 

910 "boxplot.meanprops.linestyle": _validate_linestyle, 

911 "boxplot.meanprops.linewidth": validate_float, 

912 

913 ## font props 

914 "font.family": validate_stringlist, # used by text object 

915 "font.style": validate_string, 

916 "font.variant": validate_string, 

917 "font.stretch": validate_fontstretch, 

918 "font.weight": validate_fontweight, 

919 "font.size": validate_float, # Base font size in points 

920 "font.serif": validate_stringlist, 

921 "font.sans-serif": validate_stringlist, 

922 "font.cursive": validate_stringlist, 

923 "font.fantasy": validate_stringlist, 

924 "font.monospace": validate_stringlist, 

925 

926 # text props 

927 "text.color": validate_color, 

928 "text.usetex": validate_bool, 

929 "text.latex.preamble": validate_string, 

930 "text.hinting": ["default", "no_autohint", "force_autohint", 

931 "no_hinting", "auto", "native", "either", "none"], 

932 "text.hinting_factor": validate_int, 

933 "text.kerning_factor": validate_int, 

934 "text.antialiased": validate_bool, 

935 "text.parse_math": validate_bool, 

936 

937 "mathtext.cal": validate_font_properties, 

938 "mathtext.rm": validate_font_properties, 

939 "mathtext.tt": validate_font_properties, 

940 "mathtext.it": validate_font_properties, 

941 "mathtext.bf": validate_font_properties, 

942 "mathtext.sf": validate_font_properties, 

943 "mathtext.fontset": ["dejavusans", "dejavuserif", "cm", "stix", 

944 "stixsans", "custom"], 

945 "mathtext.default": ["rm", "cal", "it", "tt", "sf", "bf", "default", 

946 "bb", "frak", "scr", "regular"], 

947 "mathtext.fallback": _validate_mathtext_fallback, 

948 

949 "image.aspect": validate_aspect, # equal, auto, a number 

950 "image.interpolation": validate_string, 

951 "image.cmap": _validate_cmap, # gray, jet, etc. 

952 "image.lut": validate_int, # lookup table 

953 "image.origin": ["upper", "lower"], 

954 "image.resample": validate_bool, 

955 # Specify whether vector graphics backends will combine all images on a 

956 # set of axes into a single composite image 

957 "image.composite_image": validate_bool, 

958 

959 # contour props 

960 "contour.negative_linestyle": _validate_linestyle, 

961 "contour.corner_mask": validate_bool, 

962 "contour.linewidth": validate_float_or_None, 

963 "contour.algorithm": ["mpl2005", "mpl2014", "serial", "threaded"], 

964 

965 # errorbar props 

966 "errorbar.capsize": validate_float, 

967 

968 # axis props 

969 # alignment of x/y axis title 

970 "xaxis.labellocation": ["left", "center", "right"], 

971 "yaxis.labellocation": ["bottom", "center", "top"], 

972 

973 # axes props 

974 "axes.axisbelow": validate_axisbelow, 

975 "axes.facecolor": validate_color, # background color 

976 "axes.edgecolor": validate_color, # edge color 

977 "axes.linewidth": validate_float, # edge linewidth 

978 

979 "axes.spines.left": validate_bool, # Set visibility of axes spines, 

980 "axes.spines.right": validate_bool, # i.e., the lines around the chart 

981 "axes.spines.bottom": validate_bool, # denoting data boundary. 

982 "axes.spines.top": validate_bool, 

983 

984 "axes.titlesize": validate_fontsize, # axes title fontsize 

985 "axes.titlelocation": ["left", "center", "right"], # axes title alignment 

986 "axes.titleweight": validate_fontweight, # axes title font weight 

987 "axes.titlecolor": validate_color_or_auto, # axes title font color 

988 # title location, axes units, None means auto 

989 "axes.titley": validate_float_or_None, 

990 # pad from axes top decoration to title in points 

991 "axes.titlepad": validate_float, 

992 "axes.grid": validate_bool, # display grid or not 

993 "axes.grid.which": ["minor", "both", "major"], # which grids are drawn 

994 "axes.grid.axis": ["x", "y", "both"], # grid type 

995 "axes.labelsize": validate_fontsize, # fontsize of x & y labels 

996 "axes.labelpad": validate_float, # space between label and axis 

997 "axes.labelweight": validate_fontweight, # fontsize of x & y labels 

998 "axes.labelcolor": validate_color, # color of axis label 

999 # use scientific notation if log10 of the axis range is smaller than the 

1000 # first or larger than the second 

1001 "axes.formatter.limits": _listify_validator(validate_int, n=2), 

1002 # use current locale to format ticks 

1003 "axes.formatter.use_locale": validate_bool, 

1004 "axes.formatter.use_mathtext": validate_bool, 

1005 # minimum exponent to format in scientific notation 

1006 "axes.formatter.min_exponent": validate_int, 

1007 "axes.formatter.useoffset": validate_bool, 

1008 "axes.formatter.offset_threshold": validate_int, 

1009 "axes.unicode_minus": validate_bool, 

1010 # This entry can be either a cycler object or a string repr of a 

1011 # cycler-object, which gets eval()'ed to create the object. 

1012 "axes.prop_cycle": validate_cycler, 

1013 # If "data", axes limits are set close to the data. 

1014 # If "round_numbers" axes limits are set to the nearest round numbers. 

1015 "axes.autolimit_mode": ["data", "round_numbers"], 

1016 "axes.xmargin": _range_validators["0 <= x <= 1"], # margin added to xaxis 

1017 "axes.ymargin": _range_validators["0 <= x <= 1"], # margin added to yaxis 

1018 'axes.zmargin': _range_validators["0 <= x <= 1"], # margin added to zaxis 

1019 

1020 "polaraxes.grid": validate_bool, # display polar grid or not 

1021 "axes3d.grid": validate_bool, # display 3d grid 

1022 

1023 # scatter props 

1024 "scatter.marker": validate_string, 

1025 "scatter.edgecolors": validate_string, 

1026 

1027 "date.epoch": _validate_date, 

1028 "date.autoformatter.year": validate_string, 

1029 "date.autoformatter.month": validate_string, 

1030 "date.autoformatter.day": validate_string, 

1031 "date.autoformatter.hour": validate_string, 

1032 "date.autoformatter.minute": validate_string, 

1033 "date.autoformatter.second": validate_string, 

1034 "date.autoformatter.microsecond": validate_string, 

1035 

1036 'date.converter': ['auto', 'concise'], 

1037 # for auto date locator, choose interval_multiples 

1038 'date.interval_multiples': validate_bool, 

1039 

1040 # legend properties 

1041 "legend.fancybox": validate_bool, 

1042 "legend.loc": _ignorecase([ 

1043 "best", 

1044 "upper right", "upper left", "lower left", "lower right", "right", 

1045 "center left", "center right", "lower center", "upper center", 

1046 "center"]), 

1047 

1048 # the number of points in the legend line 

1049 "legend.numpoints": validate_int, 

1050 # the number of points in the legend line for scatter 

1051 "legend.scatterpoints": validate_int, 

1052 "legend.fontsize": validate_fontsize, 

1053 "legend.title_fontsize": validate_fontsize_None, 

1054 # color of the legend 

1055 "legend.labelcolor": _validate_color_or_linecolor, 

1056 # the relative size of legend markers vs. original 

1057 "legend.markerscale": validate_float, 

1058 "legend.shadow": validate_bool, 

1059 # whether or not to draw a frame around legend 

1060 "legend.frameon": validate_bool, 

1061 # alpha value of the legend frame 

1062 "legend.framealpha": validate_float_or_None, 

1063 

1064 ## the following dimensions are in fraction of the font size 

1065 "legend.borderpad": validate_float, # units are fontsize 

1066 # the vertical space between the legend entries 

1067 "legend.labelspacing": validate_float, 

1068 # the length of the legend lines 

1069 "legend.handlelength": validate_float, 

1070 # the length of the legend lines 

1071 "legend.handleheight": validate_float, 

1072 # the space between the legend line and legend text 

1073 "legend.handletextpad": validate_float, 

1074 # the border between the axes and legend edge 

1075 "legend.borderaxespad": validate_float, 

1076 # the border between the axes and legend edge 

1077 "legend.columnspacing": validate_float, 

1078 "legend.facecolor": validate_color_or_inherit, 

1079 "legend.edgecolor": validate_color_or_inherit, 

1080 

1081 # tick properties 

1082 "xtick.top": validate_bool, # draw ticks on top side 

1083 "xtick.bottom": validate_bool, # draw ticks on bottom side 

1084 "xtick.labeltop": validate_bool, # draw label on top 

1085 "xtick.labelbottom": validate_bool, # draw label on bottom 

1086 "xtick.major.size": validate_float, # major xtick size in points 

1087 "xtick.minor.size": validate_float, # minor xtick size in points 

1088 "xtick.major.width": validate_float, # major xtick width in points 

1089 "xtick.minor.width": validate_float, # minor xtick width in points 

1090 "xtick.major.pad": validate_float, # distance to label in points 

1091 "xtick.minor.pad": validate_float, # distance to label in points 

1092 "xtick.color": validate_color, # color of xticks 

1093 "xtick.labelcolor": validate_color_or_inherit, # color of xtick labels 

1094 "xtick.minor.visible": validate_bool, # visibility of minor xticks 

1095 "xtick.minor.top": validate_bool, # draw top minor xticks 

1096 "xtick.minor.bottom": validate_bool, # draw bottom minor xticks 

1097 "xtick.major.top": validate_bool, # draw top major xticks 

1098 "xtick.major.bottom": validate_bool, # draw bottom major xticks 

1099 "xtick.labelsize": validate_fontsize, # fontsize of xtick labels 

1100 "xtick.direction": ["out", "in", "inout"], # direction of xticks 

1101 "xtick.alignment": ["center", "right", "left"], 

1102 

1103 "ytick.left": validate_bool, # draw ticks on left side 

1104 "ytick.right": validate_bool, # draw ticks on right side 

1105 "ytick.labelleft": validate_bool, # draw tick labels on left side 

1106 "ytick.labelright": validate_bool, # draw tick labels on right side 

1107 "ytick.major.size": validate_float, # major ytick size in points 

1108 "ytick.minor.size": validate_float, # minor ytick size in points 

1109 "ytick.major.width": validate_float, # major ytick width in points 

1110 "ytick.minor.width": validate_float, # minor ytick width in points 

1111 "ytick.major.pad": validate_float, # distance to label in points 

1112 "ytick.minor.pad": validate_float, # distance to label in points 

1113 "ytick.color": validate_color, # color of yticks 

1114 "ytick.labelcolor": validate_color_or_inherit, # color of ytick labels 

1115 "ytick.minor.visible": validate_bool, # visibility of minor yticks 

1116 "ytick.minor.left": validate_bool, # draw left minor yticks 

1117 "ytick.minor.right": validate_bool, # draw right minor yticks 

1118 "ytick.major.left": validate_bool, # draw left major yticks 

1119 "ytick.major.right": validate_bool, # draw right major yticks 

1120 "ytick.labelsize": validate_fontsize, # fontsize of ytick labels 

1121 "ytick.direction": ["out", "in", "inout"], # direction of yticks 

1122 "ytick.alignment": [ 

1123 "center", "top", "bottom", "baseline", "center_baseline"], 

1124 

1125 "grid.color": validate_color, # grid color 

1126 "grid.linestyle": _validate_linestyle, # solid 

1127 "grid.linewidth": validate_float, # in points 

1128 "grid.alpha": validate_float, 

1129 

1130 ## figure props 

1131 # figure title 

1132 "figure.titlesize": validate_fontsize, 

1133 "figure.titleweight": validate_fontweight, 

1134 

1135 # figure labels 

1136 "figure.labelsize": validate_fontsize, 

1137 "figure.labelweight": validate_fontweight, 

1138 

1139 # figure size in inches: width by height 

1140 "figure.figsize": _listify_validator(validate_float, n=2), 

1141 "figure.dpi": validate_float, 

1142 "figure.facecolor": validate_color, 

1143 "figure.edgecolor": validate_color, 

1144 "figure.frameon": validate_bool, 

1145 "figure.autolayout": validate_bool, 

1146 "figure.max_open_warning": validate_int, 

1147 "figure.raise_window": validate_bool, 

1148 

1149 "figure.subplot.left": _range_validators["0 <= x <= 1"], 

1150 "figure.subplot.right": _range_validators["0 <= x <= 1"], 

1151 "figure.subplot.bottom": _range_validators["0 <= x <= 1"], 

1152 "figure.subplot.top": _range_validators["0 <= x <= 1"], 

1153 "figure.subplot.wspace": _range_validators["0 <= x < 1"], 

1154 "figure.subplot.hspace": _range_validators["0 <= x < 1"], 

1155 

1156 "figure.constrained_layout.use": validate_bool, # run constrained_layout? 

1157 # wspace and hspace are fraction of adjacent subplots to use for space. 

1158 # Much smaller than above because we don't need room for the text. 

1159 "figure.constrained_layout.hspace": _range_validators["0 <= x < 1"], 

1160 "figure.constrained_layout.wspace": _range_validators["0 <= x < 1"], 

1161 # buffer around the axes, in inches. 

1162 'figure.constrained_layout.h_pad': validate_float, 

1163 'figure.constrained_layout.w_pad': validate_float, 

1164 

1165 ## Saving figure's properties 

1166 'savefig.dpi': validate_dpi, 

1167 'savefig.facecolor': validate_color_or_auto, 

1168 'savefig.edgecolor': validate_color_or_auto, 

1169 'savefig.orientation': ['landscape', 'portrait'], 

1170 "savefig.format": validate_string, 

1171 "savefig.bbox": validate_bbox, # "tight", or "standard" (= None) 

1172 "savefig.pad_inches": validate_float, 

1173 # default directory in savefig dialog box 

1174 "savefig.directory": _validate_pathlike, 

1175 "savefig.transparent": validate_bool, 

1176 

1177 "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg 

1178 

1179 # Set the papersize/type 

1180 "ps.papersize": _ignorecase(["auto", "letter", "legal", "ledger", 

1181 *[f"{ab}{i}" 

1182 for ab in "ab" for i in range(11)]]), 

1183 "ps.useafm": validate_bool, 

1184 # use ghostscript or xpdf to distill ps output 

1185 "ps.usedistiller": validate_ps_distiller, 

1186 "ps.distiller.res": validate_int, # dpi 

1187 "ps.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype) 

1188 "pdf.compression": validate_int, # 0-9 compression level; 0 to disable 

1189 "pdf.inheritcolor": validate_bool, # skip color setting commands 

1190 # use only the 14 PDF core fonts embedded in every PDF viewing application 

1191 "pdf.use14corefonts": validate_bool, 

1192 "pdf.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype) 

1193 

1194 "pgf.texsystem": ["xelatex", "lualatex", "pdflatex"], # latex variant used 

1195 "pgf.rcfonts": validate_bool, # use mpl's rc settings for font config 

1196 "pgf.preamble": validate_string, # custom LaTeX preamble 

1197 

1198 # write raster image data into the svg file 

1199 "svg.image_inline": validate_bool, 

1200 "svg.fonttype": ["none", "path"], # save text as text ("none") or "paths" 

1201 "svg.hashsalt": validate_string_or_None, 

1202 

1203 # set this when you want to generate hardcopy docstring 

1204 "docstring.hardcopy": validate_bool, 

1205 

1206 "path.simplify": validate_bool, 

1207 "path.simplify_threshold": _range_validators["0 <= x <= 1"], 

1208 "path.snap": validate_bool, 

1209 "path.sketch": validate_sketch, 

1210 "path.effects": validate_anylist, 

1211 "agg.path.chunksize": validate_int, # 0 to disable chunking 

1212 

1213 # key-mappings (multi-character mappings should be a list/tuple) 

1214 "keymap.fullscreen": validate_stringlist, 

1215 "keymap.home": validate_stringlist, 

1216 "keymap.back": validate_stringlist, 

1217 "keymap.forward": validate_stringlist, 

1218 "keymap.pan": validate_stringlist, 

1219 "keymap.zoom": validate_stringlist, 

1220 "keymap.save": validate_stringlist, 

1221 "keymap.quit": validate_stringlist, 

1222 "keymap.quit_all": validate_stringlist, # e.g.: "W", "cmd+W", "Q" 

1223 "keymap.grid": validate_stringlist, 

1224 "keymap.grid_minor": validate_stringlist, 

1225 "keymap.yscale": validate_stringlist, 

1226 "keymap.xscale": validate_stringlist, 

1227 "keymap.help": validate_stringlist, 

1228 "keymap.copy": validate_stringlist, 

1229 

1230 # Animation settings 

1231 "animation.html": ["html5", "jshtml", "none"], 

1232 # Limit, in MB, of size of base64 encoded animation in HTML 

1233 # (i.e. IPython notebook) 

1234 "animation.embed_limit": validate_float, 

1235 "animation.writer": validate_string, 

1236 "animation.codec": validate_string, 

1237 "animation.bitrate": validate_int, 

1238 # Controls image format when frames are written to disk 

1239 "animation.frame_format": ["png", "jpeg", "tiff", "raw", "rgba", "ppm", 

1240 "sgi", "bmp", "pbm", "svg"], 

1241 # Path to ffmpeg binary. If just binary name, subprocess uses $PATH. 

1242 "animation.ffmpeg_path": _validate_pathlike, 

1243 # Additional arguments for ffmpeg movie writer (using pipes) 

1244 "animation.ffmpeg_args": validate_stringlist, 

1245 # Path to convert binary. If just binary name, subprocess uses $PATH. 

1246 "animation.convert_path": _validate_pathlike, 

1247 # Additional arguments for convert movie writer (using pipes) 

1248 "animation.convert_args": validate_stringlist, 

1249 

1250 # Classic (pre 2.0) compatibility mode 

1251 # This is used for things that are hard to make backward compatible 

1252 # with a sane rcParam alone. This does *not* turn on classic mode 

1253 # altogether. For that use `matplotlib.style.use("classic")`. 

1254 "_internal.classic_mode": validate_bool 

1255} 

1256_hardcoded_defaults = { # Defaults not inferred from matplotlibrc.template... 

1257 # ... because they are private: 

1258 "_internal.classic_mode": False, 

1259 # ... because they are deprecated: 

1260 # No current deprecations. 

1261 # backend is handled separately when constructing rcParamsDefault. 

1262} 

1263_validators = {k: _convert_validator_spec(k, conv) 

1264 for k, conv in _validators.items()}