from __future__ import annotations import itertools import pickle from typing import Any from unittest.mock import patch, Mock from datetime import datetime, date, timedelta import numpy as np from numpy.testing import (assert_array_equal, assert_approx_equal, assert_array_almost_equal) import pytest from matplotlib import _api, cbook import matplotlib.colors as mcolors from matplotlib.cbook import delete_masked_points, strip_math class Test_delete_masked_points: def test_bad_first_arg(self): with pytest.raises(ValueError): delete_masked_points('a string', np.arange(1.0, 7.0)) def test_string_seq(self): a1 = ['a', 'b', 'c', 'd', 'e', 'f'] a2 = [1, 2, 3, np.nan, np.nan, 6] result1, result2 = delete_masked_points(a1, a2) ind = [0, 1, 2, 5] assert_array_equal(result1, np.array(a1)[ind]) assert_array_equal(result2, np.array(a2)[ind]) def test_datetime(self): dates = [datetime(2008, 1, 1), datetime(2008, 1, 2), datetime(2008, 1, 3), datetime(2008, 1, 4), datetime(2008, 1, 5), datetime(2008, 1, 6)] a_masked = np.ma.array([1, 2, 3, np.nan, np.nan, 6], mask=[False, False, True, True, False, False]) actual = delete_masked_points(dates, a_masked) ind = [0, 1, 5] assert_array_equal(actual[0], np.array(dates)[ind]) assert_array_equal(actual[1], a_masked[ind].compressed()) def test_rgba(self): a_masked = np.ma.array([1, 2, 3, np.nan, np.nan, 6], mask=[False, False, True, True, False, False]) a_rgba = mcolors.to_rgba_array(['r', 'g', 'b', 'c', 'm', 'y']) actual = delete_masked_points(a_masked, a_rgba) ind = [0, 1, 5] assert_array_equal(actual[0], a_masked[ind].compressed()) assert_array_equal(actual[1], a_rgba[ind]) class Test_boxplot_stats: def setup_method(self): np.random.seed(937) self.nrows = 37 self.ncols = 4 self.data = np.random.lognormal(size=(self.nrows, self.ncols), mean=1.5, sigma=1.75) self.known_keys = sorted([ 'mean', 'med', 'q1', 'q3', 'iqr', 'cilo', 'cihi', 'whislo', 'whishi', 'fliers', 'label' ]) self.std_results = cbook.boxplot_stats(self.data) self.known_nonbootstrapped_res = { 'cihi': 6.8161283264444847, 'cilo': -0.1489815330368689, 'iqr': 13.492709959447094, 'mean': 13.00447442387868, 'med': 3.3335733967038079, 'fliers': np.array([ 92.55467075, 87.03819018, 42.23204914, 39.29390996 ]), 'q1': 1.3597529879465153, 'q3': 14.85246294739361, 'whishi': 27.899688243699629, 'whislo': 0.042143774965502923 } self.known_bootstrapped_ci = { 'cihi': 8.939577523357828, 'cilo': 1.8692703958676578, } self.known_whis3_res = { 'whishi': 42.232049135969874, 'whislo': 0.042143774965502923, 'fliers': np.array([92.55467075, 87.03819018]), } self.known_res_percentiles = { 'whislo': 0.1933685896907924, 'whishi': 42.232049135969874 } self.known_res_range = { 'whislo': 0.042143774965502923, 'whishi': 92.554670752188699 } def test_form_main_list(self): assert isinstance(self.std_results, list) def test_form_each_dict(self): for res in self.std_results: assert isinstance(res, dict) def test_form_dict_keys(self): for res in self.std_results: assert set(res) <= set(self.known_keys) def test_results_baseline(self): res = self.std_results[0] for key, value in self.known_nonbootstrapped_res.items(): assert_array_almost_equal(res[key], value) def test_results_bootstrapped(self): results = cbook.boxplot_stats(self.data, bootstrap=10000) res = results[0] for key, value in self.known_bootstrapped_ci.items(): assert_approx_equal(res[key], value) def test_results_whiskers_float(self): results = cbook.boxplot_stats(self.data, whis=3) res = results[0] for key, value in self.known_whis3_res.items(): assert_array_almost_equal(res[key], value) def test_results_whiskers_range(self): results = cbook.boxplot_stats(self.data, whis=[0, 100]) res = results[0] for key, value in self.known_res_range.items(): assert_array_almost_equal(res[key], value) def test_results_whiskers_percentiles(self): results = cbook.boxplot_stats(self.data, whis=[5, 95]) res = results[0] for key, value in self.known_res_percentiles.items(): assert_array_almost_equal(res[key], value) def test_results_withlabels(self): labels = ['Test1', 2, 'Aardvark', 4] results = cbook.boxplot_stats(self.data, labels=labels) for lab, res in zip(labels, results): assert res['label'] == lab results = cbook.boxplot_stats(self.data) for res in results: assert 'label' not in res def test_label_error(self): labels = [1, 2] with pytest.raises(ValueError): cbook.boxplot_stats(self.data, labels=labels) def test_bad_dims(self): data = np.random.normal(size=(34, 34, 34)) with pytest.raises(ValueError): cbook.boxplot_stats(data) def test_boxplot_stats_autorange_false(self): x = np.zeros(shape=140) x = np.hstack([-25, x, 25]) bstats_false = cbook.boxplot_stats(x, autorange=False) bstats_true = cbook.boxplot_stats(x, autorange=True) assert bstats_false[0]['whislo'] == 0 assert bstats_false[0]['whishi'] == 0 assert_array_almost_equal(bstats_false[0]['fliers'], [-25, 25]) assert bstats_true[0]['whislo'] == -25 assert bstats_true[0]['whishi'] == 25 assert_array_almost_equal(bstats_true[0]['fliers'], []) class Test_callback_registry: def setup_method(self): self.signal = 'test' self.callbacks = cbook.CallbackRegistry() def connect(self, s, func, pickle): if pickle: return self.callbacks.connect(s, func) else: return self.callbacks._connect_picklable(s, func) def disconnect(self, cid): return self.callbacks.disconnect(cid) def count(self): count1 = len(self.callbacks._func_cid_map.get(self.signal, [])) count2 = len(self.callbacks.callbacks.get(self.signal)) assert count1 == count2 return count1 def is_empty(self): np.testing.break_cycles() assert self.callbacks._func_cid_map == {} assert self.callbacks.callbacks == {} assert self.callbacks._pickled_cids == set() def is_not_empty(self): np.testing.break_cycles() assert self.callbacks._func_cid_map != {} assert self.callbacks.callbacks != {} def test_cid_restore(self): cb = cbook.CallbackRegistry() cb.connect('a', lambda: None) cb2 = pickle.loads(pickle.dumps(cb)) cid = cb2.connect('c', lambda: None) assert cid == 1 @pytest.mark.parametrize('pickle', [True, False]) def test_callback_complete(self, pickle): # ensure we start with an empty registry self.is_empty() # create a class for testing mini_me = Test_callback_registry() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) assert type(cid1) is int self.is_not_empty() # test that we don't add a second callback cid2 = self.connect(self.signal, mini_me.dummy, pickle) assert cid1 == cid2 self.is_not_empty() assert len(self.callbacks._func_cid_map) == 1 assert len(self.callbacks.callbacks) == 1 del mini_me # check we now have no callbacks registered self.is_empty() @pytest.mark.parametrize('pickle', [True, False]) def test_callback_disconnect(self, pickle): # ensure we start with an empty registry self.is_empty() # create a class for testing mini_me = Test_callback_registry() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) assert type(cid1) is int self.is_not_empty() self.disconnect(cid1) # check we now have no callbacks registered self.is_empty() @pytest.mark.parametrize('pickle', [True, False]) def test_callback_wrong_disconnect(self, pickle): # ensure we start with an empty registry self.is_empty() # create a class for testing mini_me = Test_callback_registry() # test that we can add a callback cid1 = self.connect(self.signal, mini_me.dummy, pickle) assert type(cid1) is int self.is_not_empty() self.disconnect("foo") # check we still have callbacks registered self.is_not_empty() @pytest.mark.parametrize('pickle', [True, False]) def test_registration_on_non_empty_registry(self, pickle): # ensure we start with an empty registry self.is_empty() # setup the registry with a callback mini_me = Test_callback_registry() self.connect(self.signal, mini_me.dummy, pickle) # Add another callback mini_me2 = Test_callback_registry() self.connect(self.signal, mini_me2.dummy, pickle) # Remove and add the second callback mini_me2 = Test_callback_registry() self.connect(self.signal, mini_me2.dummy, pickle) # We still have 2 references self.is_not_empty() assert self.count() == 2 # Removing the last 2 references mini_me = None mini_me2 = None self.is_empty() def dummy(self): pass def test_pickling(self): assert hasattr(pickle.loads(pickle.dumps(cbook.CallbackRegistry())), "callbacks") def test_callbackregistry_default_exception_handler(capsys, monkeypatch): cb = cbook.CallbackRegistry() cb.connect("foo", lambda: None) monkeypatch.setattr( cbook, "_get_running_interactive_framework", lambda: None) with pytest.raises(TypeError): cb.process("foo", "argument mismatch") outerr = capsys.readouterr() assert outerr.out == outerr.err == "" monkeypatch.setattr( cbook, "_get_running_interactive_framework", lambda: "not-none") cb.process("foo", "argument mismatch") # No error in that case. outerr = capsys.readouterr() assert outerr.out == "" assert "takes 0 positional arguments but 1 was given" in outerr.err def raising_cb_reg(func): class TestException(Exception): pass def raise_runtime_error(): raise RuntimeError def raise_value_error(): raise ValueError def transformer(excp): if isinstance(excp, RuntimeError): raise TestException raise excp # old default cb_old = cbook.CallbackRegistry(exception_handler=None) cb_old.connect('foo', raise_runtime_error) # filter cb_filt = cbook.CallbackRegistry(exception_handler=transformer) cb_filt.connect('foo', raise_runtime_error) # filter cb_filt_pass = cbook.CallbackRegistry(exception_handler=transformer) cb_filt_pass.connect('foo', raise_value_error) return pytest.mark.parametrize('cb, excp', [[cb_old, RuntimeError], [cb_filt, TestException], [cb_filt_pass, ValueError]])(func) @raising_cb_reg def test_callbackregistry_custom_exception_handler(monkeypatch, cb, excp): monkeypatch.setattr( cbook, "_get_running_interactive_framework", lambda: None) with pytest.raises(excp): cb.process('foo') def test_callbackregistry_signals(): cr = cbook.CallbackRegistry(signals=["foo"]) results = [] def cb(x): results.append(x) cr.connect("foo", cb) with pytest.raises(ValueError): cr.connect("bar", cb) cr.process("foo", 1) with pytest.raises(ValueError): cr.process("bar", 1) assert results == [1] def test_callbackregistry_blocking(): # Needs an exception handler for interactive testing environments # that would only print this out instead of raising the exception def raise_handler(excp): raise excp cb = cbook.CallbackRegistry(exception_handler=raise_handler) def test_func1(): raise ValueError("1 should be blocked") def test_func2(): raise ValueError("2 should be blocked") cb.connect("test1", test_func1) cb.connect("test2", test_func2) # block all of the callbacks to make sure they aren't processed with cb.blocked(): cb.process("test1") cb.process("test2") # block individual callbacks to make sure the other is still processed with cb.blocked(signal="test1"): # Blocked cb.process("test1") # Should raise with pytest.raises(ValueError, match="2 should be blocked"): cb.process("test2") # Make sure the original callback functions are there after blocking with pytest.raises(ValueError, match="1 should be blocked"): cb.process("test1") with pytest.raises(ValueError, match="2 should be blocked"): cb.process("test2") @pytest.mark.parametrize('line, result', [ ('a : no_comment', 'a : no_comment'), ('a : "quoted str"', 'a : "quoted str"'), ('a : "quoted str" # comment', 'a : "quoted str"'), ('a : "#000000"', 'a : "#000000"'), ('a : "#000000" # comment', 'a : "#000000"'), ('a : ["#000000", "#FFFFFF"]', 'a : ["#000000", "#FFFFFF"]'), ('a : ["#000000", "#FFFFFF"] # comment', 'a : ["#000000", "#FFFFFF"]'), ('a : val # a comment "with quotes"', 'a : val'), ('# only comment "with quotes" xx', ''), ]) def test_strip_comment(line, result): """Strip everything from the first unquoted #.""" assert cbook._strip_comment(line) == result def test_strip_comment_invalid(): with pytest.raises(ValueError, match="Missing closing quote"): cbook._strip_comment('grid.color: "aa') def test_sanitize_sequence(): d = {'a': 1, 'b': 2, 'c': 3} k = ['a', 'b', 'c'] v = [1, 2, 3] i = [('a', 1), ('b', 2), ('c', 3)] assert k == sorted(cbook.sanitize_sequence(d.keys())) assert v == sorted(cbook.sanitize_sequence(d.values())) assert i == sorted(cbook.sanitize_sequence(d.items())) assert i == cbook.sanitize_sequence(i) assert k == cbook.sanitize_sequence(k) fail_mapping: tuple[tuple[dict, dict], ...] = ( ({'a': 1, 'b': 2}, {'alias_mapping': {'a': ['b']}}), ({'a': 1, 'b': 2}, {'alias_mapping': {'a': ['a', 'b']}}), ) pass_mapping: tuple[tuple[Any, dict, dict], ...] = ( (None, {}, {}), ({'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {}), ({'b': 2}, {'a': 2}, {'alias_mapping': {'a': ['a', 'b']}}), ) @pytest.mark.parametrize('inp, kwargs_to_norm', fail_mapping) def test_normalize_kwargs_fail(inp, kwargs_to_norm): with pytest.raises(TypeError), \ _api.suppress_matplotlib_deprecation_warning(): cbook.normalize_kwargs(inp, **kwargs_to_norm) @pytest.mark.parametrize('inp, expected, kwargs_to_norm', pass_mapping) def test_normalize_kwargs_pass(inp, expected, kwargs_to_norm): with _api.suppress_matplotlib_deprecation_warning(): # No other warning should be emitted. assert expected == cbook.normalize_kwargs(inp, **kwargs_to_norm) def test_warn_external_frame_embedded_python(): with patch.object(cbook, "sys") as mock_sys: mock_sys._getframe = Mock(return_value=None) with pytest.warns(UserWarning, match=r"\Adummy\Z"): _api.warn_external("dummy") def test_to_prestep(): x = np.arange(4) y1 = np.arange(4) y2 = np.arange(4)[::-1] xs, y1s, y2s = cbook.pts_to_prestep(x, y1, y2) x_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype=float) y1_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype=float) y2_target = np.asarray([3, 2, 2, 1, 1, 0, 0], dtype=float) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) assert_array_equal(y2_target, y2s) xs, y1s = cbook.pts_to_prestep(x, y1) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) def test_to_prestep_empty(): steps = cbook.pts_to_prestep([], []) assert steps.shape == (2, 0) def test_to_poststep(): x = np.arange(4) y1 = np.arange(4) y2 = np.arange(4)[::-1] xs, y1s, y2s = cbook.pts_to_poststep(x, y1, y2) x_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype=float) y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype=float) y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0], dtype=float) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) assert_array_equal(y2_target, y2s) xs, y1s = cbook.pts_to_poststep(x, y1) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) def test_to_poststep_empty(): steps = cbook.pts_to_poststep([], []) assert steps.shape == (2, 0) def test_to_midstep(): x = np.arange(4) y1 = np.arange(4) y2 = np.arange(4)[::-1] xs, y1s, y2s = cbook.pts_to_midstep(x, y1, y2) x_target = np.asarray([0, .5, .5, 1.5, 1.5, 2.5, 2.5, 3], dtype=float) y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3, 3], dtype=float) y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0, 0], dtype=float) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) assert_array_equal(y2_target, y2s) xs, y1s = cbook.pts_to_midstep(x, y1) assert_array_equal(x_target, xs) assert_array_equal(y1_target, y1s) def test_to_midstep_empty(): steps = cbook.pts_to_midstep([], []) assert steps.shape == (2, 0) @pytest.mark.parametrize( "args", [(np.arange(12).reshape(3, 4), 'a'), (np.arange(12), 'a'), (np.arange(12), np.arange(3))]) def test_step_fails(args): with pytest.raises(ValueError): cbook.pts_to_prestep(*args) def test_grouper(): class Dummy: pass a, b, c, d, e = objs = [Dummy() for _ in range(5)] g = cbook.Grouper() g.join(*objs) assert set(list(g)[0]) == set(objs) assert set(g.get_siblings(a)) == set(objs) for other in objs[1:]: assert g.joined(a, other) g.remove(a) for other in objs[1:]: assert not g.joined(a, other) for A, B in itertools.product(objs[1:], objs[1:]): assert g.joined(A, B) def test_grouper_private(): class Dummy: pass objs = [Dummy() for _ in range(5)] g = cbook.Grouper() g.join(*objs) # reach in and touch the internals ! mapping = g._mapping for o in objs: assert o in mapping base_set = mapping[objs[0]] for o in objs[1:]: assert mapping[o] is base_set def test_flatiter(): x = np.arange(5) it = x.flat assert 0 == next(it) assert 1 == next(it) ret = cbook._safe_first_finite(it) assert ret == 0 assert 0 == next(it) assert 1 == next(it) def test__safe_first_finite_all_nan(): arr = np.full(2, np.nan) ret = cbook._safe_first_finite(arr) assert np.isnan(ret) def test__safe_first_finite_all_inf(): arr = np.full(2, np.inf) ret = cbook._safe_first_finite(arr) assert np.isinf(ret) def test_reshape2d(): class Dummy: pass xnew = cbook._reshape_2D([], 'x') assert np.shape(xnew) == (1, 0) x = [Dummy() for _ in range(5)] xnew = cbook._reshape_2D(x, 'x') assert np.shape(xnew) == (1, 5) x = np.arange(5) xnew = cbook._reshape_2D(x, 'x') assert np.shape(xnew) == (1, 5) x = [[Dummy() for _ in range(5)] for _ in range(3)] xnew = cbook._reshape_2D(x, 'x') assert np.shape(xnew) == (3, 5) # this is strange behaviour, but... x = np.random.rand(3, 5) xnew = cbook._reshape_2D(x, 'x') assert np.shape(xnew) == (5, 3) # Test a list of lists which are all of length 1 x = [[1], [2], [3]] xnew = cbook._reshape_2D(x, 'x') assert isinstance(xnew, list) assert isinstance(xnew[0], np.ndarray) and xnew[0].shape == (1,) assert isinstance(xnew[1], np.ndarray) and xnew[1].shape == (1,) assert isinstance(xnew[2], np.ndarray) and xnew[2].shape == (1,) # Test a list of zero-dimensional arrays x = [np.array(0), np.array(1), np.array(2)] xnew = cbook._reshape_2D(x, 'x') assert isinstance(xnew, list) assert len(xnew) == 1 assert isinstance(xnew[0], np.ndarray) and xnew[0].shape == (3,) # Now test with a list of lists with different lengths, which means the # array will internally be converted to a 1D object array of lists x = [[1, 2, 3], [3, 4], [2]] xnew = cbook._reshape_2D(x, 'x') assert isinstance(xnew, list) assert isinstance(xnew[0], np.ndarray) and xnew[0].shape == (3,) assert isinstance(xnew[1], np.ndarray) and xnew[1].shape == (2,) assert isinstance(xnew[2], np.ndarray) and xnew[2].shape == (1,) # We now need to make sure that this works correctly for Numpy subclasses # where iterating over items can return subclasses too, which may be # iterable even if they are scalars. To emulate this, we make a Numpy # array subclass that returns Numpy 'scalars' when iterating or accessing # values, and these are technically iterable if checking for example # isinstance(x, collections.abc.Iterable). class ArraySubclass(np.ndarray): def __iter__(self): for value in super().__iter__(): yield np.array(value) def __getitem__(self, item): return np.array(super().__getitem__(item)) v = np.arange(10, dtype=float) x = ArraySubclass((10,), dtype=float, buffer=v.data) xnew = cbook._reshape_2D(x, 'x') # We check here that the array wasn't split up into many individual # ArraySubclass, which is what used to happen due to a bug in _reshape_2D assert len(xnew) == 1 assert isinstance(xnew[0], ArraySubclass) # check list of strings: x = ['a', 'b', 'c', 'c', 'dd', 'e', 'f', 'ff', 'f'] xnew = cbook._reshape_2D(x, 'x') assert len(xnew[0]) == len(x) assert isinstance(xnew[0], np.ndarray) def test_reshape2d_pandas(pd): # separate to allow the rest of the tests to run if no pandas... X = np.arange(30).reshape(10, 3) x = pd.DataFrame(X, columns=["a", "b", "c"]) Xnew = cbook._reshape_2D(x, 'x') # Need to check each row because _reshape_2D returns a list of arrays: for x, xnew in zip(X.T, Xnew): np.testing.assert_array_equal(x, xnew) def test_reshape2d_xarray(xr): # separate to allow the rest of the tests to run if no xarray... X = np.arange(30).reshape(10, 3) x = xr.DataArray(X, dims=["x", "y"]) Xnew = cbook._reshape_2D(x, 'x') # Need to check each row because _reshape_2D returns a list of arrays: for x, xnew in zip(X.T, Xnew): np.testing.assert_array_equal(x, xnew) def test_index_of_pandas(pd): # separate to allow the rest of the tests to run if no pandas... X = np.arange(30).reshape(10, 3) x = pd.DataFrame(X, columns=["a", "b", "c"]) Idx, Xnew = cbook.index_of(x) np.testing.assert_array_equal(X, Xnew) IdxRef = np.arange(10) np.testing.assert_array_equal(Idx, IdxRef) def test_index_of_xarray(xr): # separate to allow the rest of the tests to run if no xarray... X = np.arange(30).reshape(10, 3) x = xr.DataArray(X, dims=["x", "y"]) Idx, Xnew = cbook.index_of(x) np.testing.assert_array_equal(X, Xnew) IdxRef = np.arange(10) np.testing.assert_array_equal(Idx, IdxRef) def test_contiguous_regions(): a, b, c = 3, 4, 5 # Starts and ends with True mask = [True]*a + [False]*b + [True]*c expected = [(0, a), (a+b, a+b+c)] assert cbook.contiguous_regions(mask) == expected d, e = 6, 7 # Starts with True ends with False mask = mask + [False]*e assert cbook.contiguous_regions(mask) == expected # Starts with False ends with True mask = [False]*d + mask[:-e] expected = [(d, d+a), (d+a+b, d+a+b+c)] assert cbook.contiguous_regions(mask) == expected # Starts and ends with False mask = mask + [False]*e assert cbook.contiguous_regions(mask) == expected # No True in mask assert cbook.contiguous_regions([False]*5) == [] # Empty mask assert cbook.contiguous_regions([]) == [] def test_safe_first_element_pandas_series(pd): # deliberately create a pandas series with index not starting from 0 s = pd.Series(range(5), index=range(10, 15)) actual = cbook._safe_first_finite(s) assert actual == 0 def test_warn_external(recwarn): _api.warn_external("oops") assert len(recwarn) == 1 assert recwarn[0].filename == __file__ def test_array_patch_perimeters(): # This compares the old implementation as a reference for the # vectorized one. def check(x, rstride, cstride): rows, cols = x.shape row_inds = [*range(0, rows-1, rstride), rows-1] col_inds = [*range(0, cols-1, cstride), cols-1] polys = [] for rs, rs_next in zip(row_inds[:-1], row_inds[1:]): for cs, cs_next in zip(col_inds[:-1], col_inds[1:]): # +1 ensures we share edges between polygons ps = cbook._array_perimeter(x[rs:rs_next+1, cs:cs_next+1]).T polys.append(ps) polys = np.asarray(polys) assert np.array_equal(polys, cbook._array_patch_perimeters( x, rstride=rstride, cstride=cstride)) def divisors(n): return [i for i in range(1, n + 1) if n % i == 0] for rows, cols in [(5, 5), (7, 14), (13, 9)]: x = np.arange(rows * cols).reshape(rows, cols) for rstride, cstride in itertools.product(divisors(rows - 1), divisors(cols - 1)): check(x, rstride=rstride, cstride=cstride) def test_setattr_cm(): class A: cls_level = object() override = object() def __init__(self): self.aardvark = 'aardvark' self.override = 'override' self._p = 'p' def meth(self): ... @classmethod def classy(cls): ... @staticmethod def static(): ... @property def prop(self): return self._p @prop.setter def prop(self, val): self._p = val class B(A): ... other = A() def verify_pre_post_state(obj): # When you access a Python method the function is bound # to the object at access time so you get a new instance # of MethodType every time. # # https://docs.python.org/3/howto/descriptor.html#functions-and-methods assert obj.meth is not obj.meth # normal attribute should give you back the same instance every time assert obj.aardvark is obj.aardvark assert a.aardvark == 'aardvark' # and our property happens to give the same instance every time assert obj.prop is obj.prop assert obj.cls_level is A.cls_level assert obj.override == 'override' assert not hasattr(obj, 'extra') assert obj.prop == 'p' assert obj.monkey == other.meth assert obj.cls_level is A.cls_level assert 'cls_level' not in obj.__dict__ assert 'classy' not in obj.__dict__ assert 'static' not in obj.__dict__ a = B() a.monkey = other.meth verify_pre_post_state(a) with cbook._setattr_cm( a, prop='squirrel', aardvark='moose', meth=lambda: None, override='boo', extra='extra', monkey=lambda: None, cls_level='bob', classy='classy', static='static'): # because we have set a lambda, it is normal attribute access # and the same every time assert a.meth is a.meth assert a.aardvark is a.aardvark assert a.aardvark == 'moose' assert a.override == 'boo' assert a.extra == 'extra' assert a.prop == 'squirrel' assert a.monkey != other.meth assert a.cls_level == 'bob' assert a.classy == 'classy' assert a.static == 'static' verify_pre_post_state(a) def test_format_approx(): f = cbook._format_approx assert f(0, 1) == '0' assert f(0, 2) == '0' assert f(0, 3) == '0' assert f(-0.0123, 1) == '-0' assert f(1e-7, 5) == '0' assert f(0.0012345600001, 5) == '0.00123' assert f(-0.0012345600001, 5) == '-0.00123' assert f(0.0012345600001, 8) == f(0.0012345600001, 10) == '0.00123456' def test_safe_first_element_with_none(): datetime_lst = [date.today() + timedelta(days=i) for i in range(10)] datetime_lst[0] = None actual = cbook._safe_first_finite(datetime_lst) assert actual is not None and actual == datetime_lst[1] def test_strip_math(): assert strip_math(r'1 \times 2') == r'1 \times 2' assert strip_math(r'$1 \times 2$') == '1 x 2' assert strip_math(r'$\rm{hi}$') == 'hi' @pytest.mark.parametrize('fmt, value, result', [ ('%.2f m', 0.2, '0.20 m'), ('{:.2f} m', 0.2, '0.20 m'), ('{} m', 0.2, '0.2 m'), ('const', 0.2, 'const'), ('%d or {}', 0.2, '0 or {}'), ('{{{:,.0f}}}', 2e5, '{200,000}'), ('{:.2%}', 2/3, '66.67%'), ('$%g', 2.54, '$2.54'), ]) def test_auto_format_str(fmt, value, result): """Apply *value* to the format string *fmt*.""" assert cbook._auto_format_str(fmt, value) == result assert cbook._auto_format_str(fmt, np.float64(value)) == result