|
12 | 12 | from matplotlib import _api, _c_internal_utils |
13 | 13 | import matplotlib.pyplot as plt |
14 | 14 | import matplotlib.colors as mcolors |
| 15 | +import matplotlib.patheffects as path_effects |
| 16 | +from matplotlib.testing.decorators import check_figures_equal |
| 17 | + |
15 | 18 | import numpy as np |
16 | 19 | from matplotlib.rcsetup import ( |
| 20 | + validate_anydict, |
17 | 21 | validate_bool, |
18 | 22 | validate_color, |
19 | 23 | validate_colorlist, |
|
27 | 31 | validate_int, |
28 | 32 | validate_markevery, |
29 | 33 | validate_stringlist, |
| 34 | + validate_path_effects, |
30 | 35 | _validate_linestyle, |
31 | | - _listify_validator) |
| 36 | + _listify_validator, |
| 37 | + ) |
32 | 38 |
|
33 | 39 |
|
34 | 40 | def test_rcparams(tmpdir): |
@@ -628,3 +634,88 @@ def test_rcparams_legend_loc_from_file(tmpdir, value): |
628 | 634 |
|
629 | 635 | with mpl.rc_context(fname=rc_path): |
630 | 636 | assert mpl.rcParams["legend.loc"] == value |
| 637 | + |
| 638 | + |
| 639 | +@pytest.mark.parametrize("allow_none", [True, False]) |
| 640 | +def test_validate_dict(allow_none): |
| 641 | + fval = validate_anydict(allow_none) |
| 642 | + assert fval("{'a': 1, 'b': 2}") == {'a': 1, 'b': 2} |
| 643 | + with pytest.raises(ValueError, match=r"Input \['a', 'b'\] "): |
| 644 | + fval(['a', 'b']) |
| 645 | + |
| 646 | + fval = validate_anydict(allow_none, required_keys={'a'}) |
| 647 | + assert fval({'a': 1}) == {'a': 1} |
| 648 | + with pytest.raises(ValueError, match="Missing required key: {'a'}"): |
| 649 | + fval({'b': 1}) |
| 650 | + |
| 651 | + |
| 652 | +def test_validate_dict_none(): |
| 653 | + assert validate_anydict()(None) is None |
| 654 | + assert validate_anydict(required_keys={'a'})(None) is None |
| 655 | + |
| 656 | + with pytest.raises(ValueError, |
| 657 | + match=r"Input None must be a dictionary "): |
| 658 | + validate_anydict(False)(None) |
| 659 | + with pytest.raises(ValueError, |
| 660 | + match=r"Input 0 must be a dictionary {'k': v} or None"): |
| 661 | + validate_anydict(True)(0) |
| 662 | + |
| 663 | + |
| 664 | +ped = [{'name': 'Normal'}, |
| 665 | + {'name': 'Stroke', 'offset': (1, 2)}, |
| 666 | + {'name': 'withStroke', 'linewidth': 4, 'foreground': 'w'}] |
| 667 | + |
| 668 | +pel = [path_effects.Normal(), |
| 669 | + path_effects.Stroke((1, 2)), |
| 670 | + path_effects.withStroke(linewidth=4, foreground='w')] |
| 671 | + |
| 672 | + |
| 673 | +@pytest.mark.parametrize("value", [pel, ped], ids=["func", "dict"]) |
| 674 | +def test_path_effects(value): |
| 675 | + assert validate_path_effects(value) == value |
| 676 | + for v in value: |
| 677 | + assert validate_path_effects(value) == value |
| 678 | + |
| 679 | + |
| 680 | +def test_path_effects_string_dict(): |
| 681 | + """test list of dicts properly parsed""" |
| 682 | + pstr = "{'name': 'Normal'}," |
| 683 | + pstr += "{'name': 'Stroke', 'offset': (1, 2)}," |
| 684 | + pstr += "{'name': 'withStroke', 'linewidth': 4, 'foreground': 'w'}" |
| 685 | + assert validate_path_effects(pstr) == ped |
| 686 | + |
| 687 | + |
| 688 | +@pytest.mark.parametrize("fdict, flist", |
| 689 | + [([ped[0]], [pel[0]]), |
| 690 | + ([ped[1]], [pel[1]]), |
| 691 | + ([ped[2]], [ped[2]]), |
| 692 | + (ped, pel)], |
| 693 | + ids=['function', 'args', 'kwargs', 'all']) |
| 694 | +@check_figures_equal() |
| 695 | +def test_path_effects_picture(fig_test, fig_ref, fdict, flist): |
| 696 | + with mpl.rc_context({'path.effects': fdict}): |
| 697 | + fig_test.subplots().plot([1, 2, 3]) |
| 698 | + |
| 699 | + with mpl.rc_context({'path.effects': flist}): |
| 700 | + fig_ref.subplots().plot([1, 2, 3]) |
| 701 | + |
| 702 | + |
| 703 | +def test_path_effect_errors(): |
| 704 | + with pytest.raises(ValueError, match="Missing required key: {'name'}"): |
| 705 | + mpl.rcParams['path.effects'] = [{'kwargs': {1, 2, 3}}] |
| 706 | + |
| 707 | + with pytest.raises(ValueError, match=r"Key path.effects: Input 1 "): |
| 708 | + mpl.rcParams['path.effects'] = [1, 2, 3] |
| 709 | + |
| 710 | + |
| 711 | +def test_path_effects_from_file(tmpdir): |
| 712 | + # rcParams['legend.loc'] should be settable from matplotlibrc. |
| 713 | + # if any of these are not allowed, an exception will be raised. |
| 714 | + # test for gh issue #22338 |
| 715 | + rc_path = tmpdir.join("matplotlibrc") |
| 716 | + rc_path.write("path.effects: " |
| 717 | + "{'name': 'Normal'}, {'name': 'withStroke', 'linewidth': 2}") |
| 718 | + |
| 719 | + with mpl.rc_context(fname=rc_path): |
| 720 | + assert isinstance(mpl.rcParams["path.effects"][0], path_effects.Normal) |
| 721 | + assert isinstance(mpl.rcParams["path.effects"][1], path_effects.withStroke) |
0 commit comments