|
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