Skip to content

Commit 52b2e93

Browse files
committed
Make QuadMesh arguments with defaults keyword_only
1 parent 3bf56aa commit 52b2e93

File tree

4 files changed

+148
-20
lines changed

4 files changed

+148
-20
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
QuadMesh signature
2+
~~~~~~~~~~~~~~~~~~
3+
The ``QuadMesh`` signature ::
4+
5+
def __init__(meshWidth, meshHeight, coordinates,
6+
antialiased=True, shading=False, **kwargs)
7+
8+
is deprecated and replaced by the new signature ::
9+
10+
def __init__(coordinates, *, antialiased=True, shading=False, **kwargs)
11+
12+
In particular:
13+
14+
- *coordinates* must now be a (M, N, 2) array-like. Previously, the grid
15+
shape was separately specified as (*meshHeight* + 1, *meshWidth* + 1) and
16+
*coordnates* could be an array-like of any shape with M * N * 2 elements.
17+
- all parameters except *coordinates* are keyword-only now.

lib/matplotlib/axes/_axes.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6164,16 +6164,12 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
61646164

61656165
X, Y, C, shading = self._pcolorargs('pcolormesh', *args,
61666166
shading=shading, kwargs=kwargs)
6167-
Ny, Nx = X.shape
6168-
X = X.ravel()
6169-
Y = Y.ravel()
6170-
6171-
# convert to one dimensional arrays
6167+
coords = np.stack([X, Y], axis=-1)
6168+
# convert to one dimensional array
61726169
C = C.ravel()
6173-
coords = np.column_stack((X, Y)).astype(float, copy=False)
6174-
collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords,
6175-
antialiased=antialiased, shading=shading,
6176-
**kwargs)
6170+
6171+
collection = mcoll.QuadMesh(
6172+
coords, antialiased=antialiased, shading=shading, **kwargs)
61776173
snap = kwargs.get('snap', rcParams['pcolormesh.snap'])
61786174
collection.set_snap(snap)
61796175
collection.set_alpha(alpha)
@@ -6184,6 +6180,8 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
61846180

61856181
self.grid(False)
61866182

6183+
coords = coords.reshape(-1, 2) # flatten the grid structure; keep x, y
6184+
61876185
# Transform from native to data coordinates?
61886186
t = collection._transform
61896187
if (not isinstance(t, mtransforms.Transform) and
@@ -6360,7 +6358,7 @@ def pcolorfast(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
63606358
else:
63616359
raise ValueError("C must be 2D or 3D")
63626360
collection = mcoll.QuadMesh(
6363-
nc, nr, coords, **qm_kwargs,
6361+
coords, **qm_kwargs,
63646362
alpha=alpha, cmap=cmap, norm=norm,
63656363
antialiased=False, edgecolors="none")
63666364
self.add_collection(collection, autolim=False)

lib/matplotlib/collections.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99
line segments).
1010
"""
1111

12+
import inspect
1213
import math
1314
from numbers import Number
15+
import warnings
16+
1417
import numpy as np
1518

1619
import matplotlib as mpl
1720
from . import (_api, _path, artist, cbook, cm, colors as mcolors, docstring,
1821
hatch as mhatch, lines as mlines, path as mpath, transforms)
1922
from ._enums import JoinStyle, CapStyle
20-
import warnings
2123

2224

2325
# "color" is excluded; it is a compound setter, and its docstring differs
@@ -1991,20 +1993,59 @@ class QuadMesh(Collection):
19911993
19921994
*shading* may be 'flat', or 'gouraud'
19931995
"""
1994-
def __init__(self, meshWidth, meshHeight, coordinates,
1995-
antialiased=True, shading='flat', **kwargs):
1996+
def __init__(self, *args, **kwargs):
1997+
# signature deprecation since="3.5": Change to new signature after the
1998+
# deprecation has expired. Also remove setting __init__.__signature__,
1999+
# and remove the Notes from the docstring.
2000+
#
2001+
# We use lambdas to parse *args, **kwargs through the respective old
2002+
# and new signatures.
2003+
try:
2004+
# Old signature:
2005+
# The following raises a TypeError iif the args don't match.
2006+
w, h, coords, antialiased, shading, kwargs = (
2007+
lambda meshWidth, meshHeight, coordinates, antialiased=True,
2008+
shading=False, **kwargs:
2009+
(meshWidth, meshHeight, coordinates, antialiased, shading,
2010+
kwargs))(*args, **kwargs)
2011+
except TypeError as exc:
2012+
# New signature:
2013+
# If the following raises a TypeError (i.e. args don't match),
2014+
# just let it propagate.
2015+
coords, antialiased, shading, kwargs = (
2016+
lambda coordinates, antialiased=True, shading=False, **kwargs:
2017+
(coordinates, antialiased, shading, kwargs))(*args, **kwargs)
2018+
coords = np.asarray(coords, np.float64)
2019+
else: # The old signature matched.
2020+
_api.warn_deprecated(
2021+
"3.5",
2022+
message="This usage of Quadmesh is deprecated: Parameters "
2023+
"meshWidth and meshHights will be removed; "
2024+
"coordinates must be 2D; all parameters except "
2025+
"coordinates will be keyword-only.")
2026+
coords = np.asarray(coords, np.float64).reshape((h + 1, w + 1, 2))
2027+
# end of signature deprecation code
2028+
19962029
super().__init__(**kwargs)
1997-
self._meshWidth = meshWidth
1998-
self._meshHeight = meshHeight
1999-
# By converting to floats now, we can avoid that on every draw.
2000-
self._coordinates = np.asarray(coordinates, float).reshape(
2001-
(meshHeight + 1, meshWidth + 1, 2))
2030+
self._coordinates = coords
2031+
shape = self._coordinates.shape
2032+
if (self._coordinates.ndim != 3 or shape[-1] != 2):
2033+
raise ValueError(
2034+
"coordinates must be a (N, M, 2) array-like, but got "
2035+
f"{shape}")
2036+
2037+
self._meshWidth = shape[1] - 1
2038+
self._meshHeight = shape[0] - 1
20022039
self._antialiased = antialiased
20032040
self._shading = shading
20042041

20052042
self._bbox = transforms.Bbox.unit()
2006-
self._bbox.update_from_data_xy(coordinates.reshape(
2007-
((meshWidth + 1) * (meshHeight + 1), 2)))
2043+
self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2))
2044+
2045+
# Only needed during signature deprecation
2046+
__init__.__signature__ = inspect.signature(
2047+
lambda self, coordinates, *,
2048+
antialiased=True, shading='flat', **kwargs: None)
20082049

20092050
def get_paths(self):
20102051
if self._paths is None:

lib/matplotlib/tests/test_collections.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,78 @@ def test_singleton_autolim():
714714
np.testing.assert_allclose(ax.get_xlim(), [-0.06, 0.06])
715715

716716

717+
@pytest.mark.parametrize('flat_ref, kwargs', [
718+
(True, {}),
719+
(False, {}),
720+
(True, dict(antialiased=False)),
721+
(False, dict(transform='__initialization_delayed__')),
722+
])
723+
@check_figures_equal(extensions=['png'])
724+
def test_quadmesh_deprecated_signature(
725+
fig_test, fig_ref, flat_ref, kwargs):
726+
# test that the new and old quadmesh signature produce the same results
727+
# remove when the old QuadMesh.__init__ signature expires (v3.5+2)
728+
from matplotlib.collections import QuadMesh
729+
730+
x = [0, 1, 2, 3.]
731+
y = [1, 2, 3.]
732+
X, Y = np.meshgrid(x, y)
733+
X += 0.2 * Y
734+
coords = np.stack([X, Y], axis=-1)
735+
assert coords.shape == (3, 4, 2)
736+
C = np.linspace(0, 2, 12).reshape(3, 4)
737+
738+
ax = fig_test.add_subplot()
739+
ax.set(xlim=(0, 5), ylim=(0, 4))
740+
if 'transform' in kwargs:
741+
kwargs['transform'] = mtransforms.Affine2D().scale(1.2) + ax.transData
742+
qmesh = QuadMesh(coords, **kwargs)
743+
qmesh.set_array(C)
744+
ax.add_collection(qmesh)
745+
746+
ax = fig_ref.add_subplot()
747+
ax.set(xlim=(0, 5), ylim=(0, 4))
748+
if 'transform' in kwargs:
749+
kwargs['transform'] = mtransforms.Affine2D().scale(1.2) + ax.transData
750+
with pytest.warns(MatplotlibDeprecationWarning):
751+
qmesh = QuadMesh(4 - 1, 3 - 1,
752+
coords.copy().reshape(-1, 2) if flat_ref else coords,
753+
**kwargs)
754+
qmesh.set_array(C.flatten() if flat_ref else C)
755+
ax.add_collection(qmesh)
756+
757+
758+
@check_figures_equal(extensions=['png'])
759+
def test_quadmesh_deprecated_positional(fig_test, fig_ref):
760+
# test that positional parameters are still accepted with the old signature
761+
# and work correctly
762+
# remove when the old QuadMesh.__init__ signature expires (v3.5+2)
763+
from matplotlib.collections import QuadMesh
764+
765+
x = [0, 1, 2, 3.]
766+
y = [1, 2, 3.]
767+
X, Y = np.meshgrid(x, y)
768+
X += 0.2 * Y
769+
coords = np.stack([X, Y], axis=-1)
770+
assert coords.shape == (3, 4, 2)
771+
coords_flat = coords.copy().reshape(-1, 2)
772+
C = np.linspace(0, 2, 12).reshape(3, 4)
773+
774+
ax = fig_test.add_subplot()
775+
ax.set(xlim=(0, 5), ylim=(0, 4))
776+
qmesh = QuadMesh(coords, antialiased=False, shading='gouraud')
777+
qmesh.set_array(C)
778+
ax.add_collection(qmesh)
779+
780+
ax = fig_ref.add_subplot()
781+
ax.set(xlim=(0, 5), ylim=(0, 4))
782+
with pytest.warns(MatplotlibDeprecationWarning):
783+
qmesh = QuadMesh(4 - 1, 3 - 1, coords.copy().reshape(-1, 2),
784+
False, 'gouraud')
785+
qmesh.set_array(C)
786+
ax.add_collection(qmesh)
787+
788+
717789
def test_quadmesh_set_array():
718790
x = np.arange(4)
719791
y = np.arange(4)

0 commit comments

Comments
 (0)