You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			289 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			289 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
import re
 | 
						|
import sys
 | 
						|
 | 
						|
import numpy as np
 | 
						|
import pytest
 | 
						|
 | 
						|
from matplotlib import _preprocess_data
 | 
						|
from matplotlib.axes import Axes
 | 
						|
from matplotlib.testing import subprocess_run_for_testing
 | 
						|
from matplotlib.testing.decorators import check_figures_equal
 | 
						|
 | 
						|
# Notes on testing the plotting functions itself
 | 
						|
# *   the individual decorated plotting functions are tested in 'test_axes.py'
 | 
						|
# *   that pyplot functions accept a data kwarg is only tested in
 | 
						|
#     test_axes.test_pie_linewidth_0
 | 
						|
 | 
						|
 | 
						|
# this gets used in multiple tests, so define it here
 | 
						|
@_preprocess_data(replace_names=["x", "y"], label_namer="y")
 | 
						|
def plot_func(ax, x, y, ls="x", label=None, w="xyz"):
 | 
						|
    return f"x: {list(x)}, y: {list(y)}, ls: {ls}, w: {w}, label: {label}"
 | 
						|
 | 
						|
 | 
						|
all_funcs = [plot_func]
 | 
						|
all_func_ids = ['plot_func']
 | 
						|
 | 
						|
 | 
						|
def test_compiletime_checks():
 | 
						|
    """Test decorator invocations -> no replacements."""
 | 
						|
 | 
						|
    def func(ax, x, y): pass
 | 
						|
    def func_args(ax, x, y, *args): pass
 | 
						|
    def func_kwargs(ax, x, y, **kwargs): pass
 | 
						|
    def func_no_ax_args(*args, **kwargs): pass
 | 
						|
 | 
						|
    # this is ok
 | 
						|
    _preprocess_data(replace_names=["x", "y"])(func)
 | 
						|
    _preprocess_data(replace_names=["x", "y"])(func_kwargs)
 | 
						|
    # this has "enough" information to do all the replaces
 | 
						|
    _preprocess_data(replace_names=["x", "y"])(func_args)
 | 
						|
 | 
						|
    # no positional_parameter_names but needed due to replaces
 | 
						|
    with pytest.raises(AssertionError):
 | 
						|
        # z is unknown
 | 
						|
        _preprocess_data(replace_names=["x", "y", "z"])(func_args)
 | 
						|
 | 
						|
    # no replacements at all -> all ok...
 | 
						|
    _preprocess_data(replace_names=[], label_namer=None)(func)
 | 
						|
    _preprocess_data(replace_names=[], label_namer=None)(func_args)
 | 
						|
    _preprocess_data(replace_names=[], label_namer=None)(func_kwargs)
 | 
						|
    _preprocess_data(replace_names=[], label_namer=None)(func_no_ax_args)
 | 
						|
 | 
						|
    # label namer is unknown
 | 
						|
    with pytest.raises(AssertionError):
 | 
						|
        _preprocess_data(label_namer="z")(func)
 | 
						|
 | 
						|
    with pytest.raises(AssertionError):
 | 
						|
        _preprocess_data(label_namer="z")(func_args)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
 | 
						|
def test_function_call_without_data(func):
 | 
						|
    """Test without data -> no replacements."""
 | 
						|
    assert (func(None, "x", "y") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: None")
 | 
						|
    assert (func(None, x="x", y="y") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: None")
 | 
						|
    assert (func(None, "x", "y", label="") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, "x", "y", label="text") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: text")
 | 
						|
    assert (func(None, x="x", y="y", label="") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, x="x", y="y", label="text") ==
 | 
						|
            "x: ['x'], y: ['y'], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
 | 
						|
def test_function_call_with_dict_input(func):
 | 
						|
    """Tests with dict input, unpacking via preprocess_pipeline"""
 | 
						|
    data = {'a': 1, 'b': 2}
 | 
						|
    assert (func(None, data.keys(), data.values()) ==
 | 
						|
            "x: ['a', 'b'], y: [1, 2], ls: x, w: xyz, label: None")
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
 | 
						|
def test_function_call_with_dict_data(func):
 | 
						|
    """Test with dict data -> label comes from the value of 'x' parameter."""
 | 
						|
    data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
 | 
						|
    assert (func(None, "a", "b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, x="a", y="b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, "a", "b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, "a", "b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
    assert (func(None, x="a", y="b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, x="a", y="b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
 | 
						|
def test_function_call_with_dict_data_not_in_data(func):
 | 
						|
    """Test the case that one var is not in data -> half replaces, half kept"""
 | 
						|
    data = {"a": [1, 2], "w": "NOT"}
 | 
						|
    assert (func(None, "a", "b", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, x="a", y="b", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, "a", "b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, "a", "b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text")
 | 
						|
    assert (func(None, x="a", y="b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, x="a", y="b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize('func', all_funcs, ids=all_func_ids)
 | 
						|
def test_function_call_with_pandas_data(func, pd):
 | 
						|
    """Test with pandas dataframe -> label comes from ``data["col"].name``."""
 | 
						|
    data = pd.DataFrame({"a": np.array([1, 2], dtype=np.int32),
 | 
						|
                         "b": np.array([8, 9], dtype=np.int32),
 | 
						|
                         "w": ["NOT", "NOT"]})
 | 
						|
 | 
						|
    assert (func(None, "a", "b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, x="a", y="b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func(None, "a", "b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, "a", "b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
    assert (func(None, x="a", y="b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (func(None, x="a", y="b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
def test_function_call_replace_all():
 | 
						|
    """Test without a "replace_names" argument, all vars should be replaced."""
 | 
						|
    data = {"a": [1, 2], "b": [8, 9], "x": "xyz"}
 | 
						|
 | 
						|
    @_preprocess_data(label_namer="y")
 | 
						|
    def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"):
 | 
						|
        return f"x: {list(x)}, y: {list(y)}, ls: {ls}, w: {w}, label: {label}"
 | 
						|
 | 
						|
    assert (func_replace_all(None, "a", "b", w="x", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func_replace_all(None, x="a", y="b", w="x", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b")
 | 
						|
    assert (func_replace_all(None, "a", "b", w="x", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (
 | 
						|
        func_replace_all(None, "a", "b", w="x", label="text", data=data) ==
 | 
						|
        "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
    assert (
 | 
						|
        func_replace_all(None, x="a", y="b", w="x", label="", data=data) ==
 | 
						|
        "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (
 | 
						|
        func_replace_all(None, x="a", y="b", w="x", label="text", data=data) ==
 | 
						|
        "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
def test_no_label_replacements():
 | 
						|
    """Test with "label_namer=None" -> no label replacement at all."""
 | 
						|
 | 
						|
    @_preprocess_data(replace_names=["x", "y"], label_namer=None)
 | 
						|
    def func_no_label(ax, x, y, ls="x", label=None, w="xyz"):
 | 
						|
        return f"x: {list(x)}, y: {list(y)}, ls: {ls}, w: {w}, label: {label}"
 | 
						|
 | 
						|
    data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
 | 
						|
    assert (func_no_label(None, "a", "b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None")
 | 
						|
    assert (func_no_label(None, x="a", y="b", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None")
 | 
						|
    assert (func_no_label(None, "a", "b", label="", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ")
 | 
						|
    assert (func_no_label(None, "a", "b", label="text", data=data) ==
 | 
						|
            "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text")
 | 
						|
 | 
						|
 | 
						|
def test_more_args_than_pos_parameter():
 | 
						|
    @_preprocess_data(replace_names=["x", "y"], label_namer="y")
 | 
						|
    def func(ax, x, y, z=1):
 | 
						|
        pass
 | 
						|
 | 
						|
    data = {"a": [1, 2], "b": [8, 9], "w": "NOT"}
 | 
						|
    with pytest.raises(TypeError):
 | 
						|
        func(None, "a", "b", "z", "z", data=data)
 | 
						|
 | 
						|
 | 
						|
def test_docstring_addition():
 | 
						|
    @_preprocess_data()
 | 
						|
    def funcy(ax, *args, **kwargs):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        data : indexable object, optional
 | 
						|
            DATA_PARAMETER_PLACEHOLDER
 | 
						|
        """
 | 
						|
 | 
						|
    assert re.search(r"all parameters also accept a string", funcy.__doc__)
 | 
						|
    assert not re.search(r"the following parameters", funcy.__doc__)
 | 
						|
 | 
						|
    @_preprocess_data(replace_names=[])
 | 
						|
    def funcy(ax, x, y, z, bar=None):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        data : indexable object, optional
 | 
						|
            DATA_PARAMETER_PLACEHOLDER
 | 
						|
        """
 | 
						|
 | 
						|
    assert not re.search(r"all parameters also accept a string", funcy.__doc__)
 | 
						|
    assert not re.search(r"the following parameters", funcy.__doc__)
 | 
						|
 | 
						|
    @_preprocess_data(replace_names=["bar"])
 | 
						|
    def funcy(ax, x, y, z, bar=None):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        data : indexable object, optional
 | 
						|
            DATA_PARAMETER_PLACEHOLDER
 | 
						|
        """
 | 
						|
 | 
						|
    assert not re.search(r"all parameters also accept a string", funcy.__doc__)
 | 
						|
    assert not re.search(r"the following parameters .*: \*bar\*\.",
 | 
						|
                         funcy.__doc__)
 | 
						|
 | 
						|
    @_preprocess_data(replace_names=["x", "t"])
 | 
						|
    def funcy(ax, x, y, z, t=None):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        data : indexable object, optional
 | 
						|
            DATA_PARAMETER_PLACEHOLDER
 | 
						|
        """
 | 
						|
 | 
						|
    assert not re.search(r"all parameters also accept a string", funcy.__doc__)
 | 
						|
    assert not re.search(r"the following parameters .*: \*x\*, \*t\*\.",
 | 
						|
                         funcy.__doc__)
 | 
						|
 | 
						|
 | 
						|
def test_data_parameter_replacement():
 | 
						|
    """
 | 
						|
    Test that the docstring contains the correct *data* parameter stub
 | 
						|
    for all methods that we run _preprocess_data() on.
 | 
						|
    """
 | 
						|
    program = (
 | 
						|
        "import logging; "
 | 
						|
        "logging.basicConfig(level=logging.DEBUG); "
 | 
						|
        "import matplotlib.pyplot as plt"
 | 
						|
    )
 | 
						|
    cmd = [sys.executable, "-c", program]
 | 
						|
    completed_proc = subprocess_run_for_testing(
 | 
						|
        cmd, text=True, capture_output=True
 | 
						|
    )
 | 
						|
    assert 'data parameter docstring error' not in completed_proc.stderr
 | 
						|
 | 
						|
 | 
						|
class TestPlotTypes:
 | 
						|
 | 
						|
    plotters = [Axes.scatter, Axes.bar, Axes.plot]
 | 
						|
 | 
						|
    @pytest.mark.parametrize('plotter', plotters)
 | 
						|
    @check_figures_equal(extensions=['png'])
 | 
						|
    def test_dict_unpack(self, plotter, fig_test, fig_ref):
 | 
						|
        x = [1, 2, 3]
 | 
						|
        y = [4, 5, 6]
 | 
						|
        ddict = dict(zip(x, y))
 | 
						|
 | 
						|
        plotter(fig_test.subplots(),
 | 
						|
                ddict.keys(), ddict.values())
 | 
						|
        plotter(fig_ref.subplots(), x, y)
 | 
						|
 | 
						|
    @pytest.mark.parametrize('plotter', plotters)
 | 
						|
    @check_figures_equal(extensions=['png'])
 | 
						|
    def test_data_kwarg(self, plotter, fig_test, fig_ref):
 | 
						|
        x = [1, 2, 3]
 | 
						|
        y = [4, 5, 6]
 | 
						|
 | 
						|
        plotter(fig_test.subplots(), 'xval', 'yval',
 | 
						|
                data={'xval': x, 'yval': y})
 | 
						|
        plotter(fig_ref.subplots(), x, y)
 |