|
14 | 14 | :file:`matplotlibrc.template` in matplotlib's root source directory. |
15 | 15 | """ |
16 | 16 |
|
| 17 | +import ast |
17 | 18 | from collections.abc import Iterable, Mapping |
18 | 19 | from functools import partial, reduce |
19 | 20 | import logging |
| 21 | +from numbers import Number |
20 | 22 | import operator |
21 | 23 | import os |
22 | 24 | import re |
23 | 25 |
|
| 26 | +import numpy as np |
| 27 | + |
24 | 28 | import matplotlib as mpl |
25 | 29 | from matplotlib import animation, cbook |
26 | 30 | from matplotlib.cbook import ls_mapper |
@@ -532,6 +536,50 @@ def validate_ps_distiller(s): |
532 | 536 | 'ghostscript or xpdf') |
533 | 537 |
|
534 | 538 |
|
| 539 | +# A validator dedicated to the named line styles, based on the items in |
| 540 | +# ls_mapper, and a list of possible strings read from Line2D.set_linestyle |
| 541 | +_validate_named_linestyle = ValidateInStrings( |
| 542 | + 'linestyle', |
| 543 | + [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''], |
| 544 | + ignorecase=True) |
| 545 | + |
| 546 | + |
| 547 | +def _validate_linestyle(ls): |
| 548 | + """ |
| 549 | + A validator for all possible line styles, the named ones *and* |
| 550 | + the on-off ink sequences. |
| 551 | + """ |
| 552 | + if isinstance(ls, str): |
| 553 | + try: # Look first for a valid named line style, like '--' or 'solid'. |
| 554 | + return _validate_named_linestyle(ls) |
| 555 | + except ValueError: |
| 556 | + pass |
| 557 | + try: |
| 558 | + ls = ast.literal_eval(ls) # Parsing matplotlibrc. |
| 559 | + except (SyntaxError, ValueError): |
| 560 | + pass # Will error with the ValueError at the end. |
| 561 | + |
| 562 | + def _is_iterable_not_string_like(x): |
| 563 | + # Explicitly exclude bytes/bytearrays so that they are not |
| 564 | + # nonsensically interpreted as sequences of numbers (codepoints). |
| 565 | + return np.iterable(x) and not isinstance(x, (str, bytes, bytearray)) |
| 566 | + |
| 567 | + # (offset, (on, off, on, off, ...)) |
| 568 | + if (_is_iterable_not_string_like(ls) |
| 569 | + and len(ls) == 2 |
| 570 | + and isinstance(ls[0], (type(None), Number)) |
| 571 | + and _is_iterable_not_string_like(ls[1]) |
| 572 | + and len(ls[1]) % 2 == 0 |
| 573 | + and all(isinstance(elem, Number) for elem in ls[1])): |
| 574 | + return ls |
| 575 | + # For backcompat: (on, off, on, off, ...); the offset is implicitly None. |
| 576 | + if (_is_iterable_not_string_like(ls) |
| 577 | + and len(ls) % 2 == 0 |
| 578 | + and all(isinstance(elem, Number) for elem in ls)): |
| 579 | + return (None, ls) |
| 580 | + raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") |
| 581 | + |
| 582 | + |
535 | 583 | validate_joinstyle = ValidateInStrings('joinstyle', |
536 | 584 | ['miter', 'round', 'bevel'], |
537 | 585 | ignorecase=True) |
@@ -748,7 +796,7 @@ def validate_hatch(s): |
748 | 796 | 'color': _listify_validator(validate_color_for_prop_cycle, |
749 | 797 | allow_stringlist=True), |
750 | 798 | 'linewidth': validate_floatlist, |
751 | | - 'linestyle': validate_stringlist, |
| 799 | + 'linestyle': _listify_validator(_validate_linestyle), |
752 | 800 | 'facecolor': validate_colorlist, |
753 | 801 | 'edgecolor': validate_colorlist, |
754 | 802 | 'joinstyle': validate_joinstylelist, |
@@ -970,43 +1018,6 @@ def validate_webagg_address(s): |
970 | 1018 | raise ValueError("'webagg.address' is not a valid IP address") |
971 | 1019 |
|
972 | 1020 |
|
973 | | -# A validator dedicated to the named line styles, based on the items in |
974 | | -# ls_mapper, and a list of possible strings read from Line2D.set_linestyle |
975 | | -_validate_named_linestyle = ValidateInStrings( |
976 | | - 'linestyle', |
977 | | - [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''], |
978 | | - ignorecase=True) |
979 | | - |
980 | | - |
981 | | -def _validate_linestyle(ls): |
982 | | - """ |
983 | | - A validator for all possible line styles, the named ones *and* |
984 | | - the on-off ink sequences. |
985 | | - """ |
986 | | - # Look first for a valid named line style, like '--' or 'solid' Also |
987 | | - # includes bytes(-arrays) here (they all fail _validate_named_linestyle); |
988 | | - # otherwise, if *ls* is of even-length, it will be passed to the instance |
989 | | - # of validate_nseq_float, which will return an absurd on-off ink |
990 | | - # sequence... |
991 | | - if isinstance(ls, (str, bytes, bytearray)): |
992 | | - return _validate_named_linestyle(ls) |
993 | | - |
994 | | - # Look for an on-off ink sequence (in points) *of even length*. |
995 | | - # Offset is set to None. |
996 | | - try: |
997 | | - if len(ls) % 2 != 0: |
998 | | - raise ValueError("the linestyle sequence {!r} is not of even " |
999 | | - "length.".format(ls)) |
1000 | | - |
1001 | | - return (None, validate_nseq_float()(ls)) |
1002 | | - |
1003 | | - except (ValueError, TypeError): |
1004 | | - # TypeError can be raised inside the instance of validate_nseq_float, |
1005 | | - # by wrong types passed to float(), like NoneType. |
1006 | | - raise ValueError("linestyle {!r} is not a valid on-off ink " |
1007 | | - "sequence.".format(ls)) |
1008 | | - |
1009 | | - |
1010 | 1021 | validate_axes_titlelocation = ValidateInStrings('axes.titlelocation', ['left', 'center', 'right']) |
1011 | 1022 |
|
1012 | 1023 | # a map from key -> value, converter |
|
0 commit comments