10000 Don't check length of arguments in quiver collection and increase cov… · matplotlib/matplotlib@b5d3c16 · GitHub
[go: up one dir, main page]

Skip to content

Commit b5d3c16

Browse files
committed
Don't check length of arguments in quiver collection and increase coverage
1 parent aba8d9b commit b5d3c16

File tree

3 files changed

+88
-97
lines changed

3 files changed

+88
-97
lines changed

lib/matplotlib/quiver.py

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,10 @@ class Quiver(mcollections.PolyCollection):
454454
"""
455455
Specialized PolyCollection for arrows.
456456
457-
The API methods are set_UVC(), set_U(), set_V() and set_C(), which
458-
can be used to change the size, orientation, and color of the
459-
arrows; their locations are fixed when the class is
460-
instantiated. Possibly these methods will be useful
461-
in animations.
457+
The API methods are set_XYUVC(), set_X(), set_Y(), set_U() and set_V(),
458+
which can be used to change the size, orientation, and color of the
459+
arrows; their locations are fixed when the class is instantiated.
460+
Possibly these methods will be useful in animations.
462461
463462
Much of the work in this class is done in the draw()
464463
method so that as much information as possible is available
@@ -512,7 +511,7 @@ def __init__(self, ax, *args,
512511
X, Y, U, V, C, self._nr, self._nc = _parse_args(
513512
*args, caller_name='quiver()'
514513
)
515-
self.set_XYUVC(X=X, Y=Y, U=U, V=V, C=C)
514+
self.set_XYUVC(X=X, Y=Y, U=U, V=V, C=C, check_shape=True)
516515
self._dpi_at_last_init = None
517516

518517
def _init(self):
@@ -547,59 +546,49 @@ def X(self):
547546
return self.get_X()
548547

549548
@X.setter
550-
def X(self):
549+
def X(self, value):
551550
_api.warn_deprecated("3.9", alternative="set_X")
552-
return self.set_X()
551+
return self.set_X(value)
553552

554553
@property
555554
def Y(self):
556555
_api.warn_deprecated("3.9", alternative="get_Y")
557556
return self.get_Y()
558557

559558
@Y.setter
560-
def Y(self):
559+
def Y(self, value):
561560
_api.warn_deprecated("3.9", alternative="set_Y")
562-
return self.set_Y()
561+
return self.set_Y(value)
563562

564563
@property
565564
def U(self):
566565
_api.warn_deprecated("3.9", alternative="get_U")
567566
return self.get_U()
568567

569568
@U.setter
570-
def U(self):
569+
def U(self, value):
571570
_api.warn_deprecated("3.9", alternative="set_U")
572-
return self.set_U()
571+
return self.set_U(value)
573572

574573
@property
575574
def V(self):
576575
_api.warn_deprecated("3.9", alternative="get_V")
577576
return self.get_V()
578577

579578
@V.setter
580-
def V(self):
579+
def V(self, value):
581580
_api.warn_deprecated("3.9", alternative="set_V")
582-
return self.set_V()
583-
584-
@property
585-
def C(self):
586-
_api.warn_deprecated("3.9", alternative="get_C")
587-
return self.get_C()
588-
589-
@C.setter
590-
def C(self):
591-
_api.warn_deprecated("3.9", alternative="set_C")
592-
return self.set_C()
581+
return self.set_V(value)
593582

594583
@property
595584
def XY(self):
596-
_api.warn_deprecated("3.9", alternative="get_XY")
585+
_api.warn_deprecated("3.9", alternative="get_offsets")
597586
return self.get_offsets()
598587

599588
@XY.setter
600-
def XY(self, XY):
601-
_api.warn_deprecated("3.9", alternative="set_XY")
602-
self.set_offsets(offsets=XY)
589+
def XY(self, value):
590+
_api.warn_deprecated("3.9", alternative="set_offsets")
591+
self.set_offsets(offsets=value)
603592

604593
def set_offsets(self, offsets):
605594
self.set_XYUVC(X=offsets[:, 0], Y=offsets[:, 1])
@@ -621,23 +610,6 @@ def draw(self, renderer):
621610
super().draw(renderer)
622611
self.stale = False
623612

624-
def get_XY(self):
625-
"""Returns the positions. Alias for ``get_offsets``."""
626-
return self.get_offsets()
627-
628-
def set_XY(self, XY):
629-
"""
630-
Set positions. Alias for ``set_offsets``. If the size
631-
changes and it is not compatible with ``U``, ``V`` or
632-
``C``, use ``set_XYUVC`` instead.
633-
634-
Parameters
635-
----------
636-
X : array-like
637-
The size must be compatible with ``U``, ``V`` and ``C``.
638-
"""
639-
self.set_offsets(offsets=XY)
640-
641613
def set_X(self, X):
642614
"""
643615
Set positions in the horizontal direction.
@@ -698,22 +670,7 @@ def get_V(self):
698670
"""Returns the vertical direction components."""
699671
return self._V
700672

701-
def set_C(self, C):
702-
"""
703-
Set the arrow colors.
704-
705-
Parameters
706-
----------
707-
C : array-like
708-
The size must the same as the existing X, Y, U, V or be one.
709-
"""
710-
self.set_XYUVC(C=C)
711-
712-
def get_C(self):
713-
"""Returns the arrow colors."""
714-
return self._C
715-
716-
def set_XYUVC(self, X=None, Y=None, U=None, V=None, C=None):
673+
def set_XYUVC(self, X=None, Y=None, U=None, V=None, C=None, check_shape=False):
717674
"""
718675
Set the positions (X, Y) and components (U, V) of the arrow vectors
719676
and arrow colors (C) values of the arrows.
@@ -733,6 +690,9 @@ def set_XYUVC(self, X=None, Y=None, U=None, V=None, C=None):
733690
C : array-like or None, optional
734691
The arrow colors. The default is None.
735692
The size must the same as the existing U, V or be one.
693+
check_shape : bool
694+
Whether to check if the shape of the parameters are
695+
consistent. Default is False.
736696
"""
737697

738698
X = self.get_X() if X is None else X
@@ -752,13 +712,14 @@ def set_XYUVC(self, X=None, Y=None, U=None, V=None, C=None):
752712
C = ma.masked_invalid(
753713
self._C if C is None else C, copy=True
754714
).ravel()
755-
for name, var in zip(('U', 'V', 'C'), (U, V, C)):
756-
if not (var is None or var.size == N or var.size == 1):
757-
raise ValueError(
758-
f'Argument {name} has a size {var.size}'
759-
f' which does not match {N},'
760-
' the number of arrow positions'
761-
)
715+
if check_shape:
716+
for name, var in zip(('U', 'V', 'C'), (U, V, C)):
717+
if not (var is None or var.size == N or var.size == 1):
718+
raise ValueError(
719+
f'Argument {name} has a size {var.size}'
720+
f' which does not match {N},'
721+
' the number of arrow positions'
722+
)
762723

763724
# now shapes are validated and we can start assigning things
764725
mask = ma.mask_or(U.mask, V.mask, copy=False, shrink=True)

lib/matplotlib/quiver.pyi

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,6 @@ class Quiver(mcollections.PolyCollection):
125125
def N(self) -> int: ...
126126
def get_datalim(self, transData: Transform) -> Bbox: ...
127127
def set_offsets(self, offsets: ArrayLike) -> None: ...
128-
def set_XY(self, XY: ArrayLike) -> None: ...
129-
def get_XY(self) -> ArrayLike: ...
130128
def set_X(self, X: ArrayLike) -> None: ...
131129
def get_X(self) -> ArrayLike: ...
132130
def set_Y(self, Y: ArrayLike) -> None: ...
@@ -135,15 +133,14 @@ class Quiver(mcollections.PolyCollection):
135133
def get_U(self) -> ArrayLike: ...
136134
def set_V(self, V: ArrayLike) -> None: ...
137135
def get_V(self) -> ArrayLike: ...
138-
def set_C(self, C: ArrayLike) -> None: ...
139-
def get_C(self) -> ArrayLike: ...
140136
def set_XYUVC(
141137
self,
142138
X: ArrayLike | None = ...,
143139
Y: ArrayLike | None = ...,
144140
U: ArrayLike | None = ...,
145141
V: ArrayLike | None = ...,
146-
C: ArrayLike | None = ...
142+
C: ArrayLike | None = ...,
143+
check_shape: bool = ...,
147144
) -> None: ...
148145

149146
class Barbs(mcollections.PolyCollection):

lib/matplotlib/tests/test_collections.py

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -389,17 +389,9 @@ def test_quiver_offsets():
389389

390390
# new length
391391
L = 2
392-
with pytest.raises(ValueError):
393-
qc.set_X(qc.get_X()[:L])
394-
395-
with pytest.raises(ValueError):
396-
qc.set_Y(qc.get_Y()[:L])
397-
398-
with pytest.raises(ValueError):
399-
qc.set_offsets(qc.get_offsets()[:L])
400-
401-
with pytest.raises(ValueError):
402-
qc.set_XYUVC(X=new_X[:L], Y=new_Y[:L])
392+
qc.set_XYUVC(X=new_X[:L], Y=new_Y[:L])
393+
np.testing.assert_allclose(qc.get_X(), new_X[:L])
394+
np.testing.assert_allclose(qc.get_Y(), new_Y[:L])
403395

404396
qc.set_XYUVC(X=X[:L], Y=Y[:L], U=qc.get_U()[:L], V=qc.get_V()[:L])
405397
np.testing.assert_allclose(qc.get_X(), X[:L])
@@ -408,7 +400,7 @@ def test_quiver_offsets():
408400
np.testing.assert_allclose(qc.get_V(), V.ravel()[:L])
409401

410402

411-
def test_quiver_change_UVC():
403+
def test_quiver_change_XYUVC():
412404
fig, ax = plt.subplots()
413405
X = np.arange(-10, 10, 1)
414406
Y = np.arange(-10, 10, 1)
@@ -424,7 +416,7 @@ def test_quiver_change_UVC():
424416
np.testing.assert_allclose(qc.get_V(), V.ravel())
425417
np.testing.assert_allclose(qc.get_array(), M.ravel())
426418

427-
qc.set_XYUVC(U=U/2, V=V/3)
419+
qc.set(U=U/2, V=V/3)
428420
np.testing.assert_allclose(qc.get_U(), U.ravel() / 2)
429421
np.testing.assert_allclose(qc.get_V(), V.ravel() / 3)
430422

@@ -434,23 +426,64 @@ def test_quiver_change_UVC():
434426
qc.set_V(V/6)
435427
np.testing.assert_allclose(qc.get_V(), V.ravel() / 6)
436428

437-
qc.set_C(C=M/10)
438-
np.testing.assert_allclose(qc.get_array(), M.ravel() / 10)
439-
440-
with pytest.raises(ValueError):
441-
qc.set_X(X[:2])
442-
with pytest.raises(ValueError):
443-
qc.set_Y(Y[:2])
444-
with pytest.raises(ValueError):
445-
qc.set_U(U[:2])
429+
# check consistency not enable
430+
qc.set_XYUVC(X=X[:2], Y=Y[:2])
446431
with pytest.raises(ValueError):
447-
qc.set_V(V[:2])
432+
# setting only one of the two X, Y fails because X and Y needs
433+
# to be stacked when passed to `offsets`
434+
qc.set(Y=Y[:3])
448435

449-
qc.set_XYUVC()
436+
qc.set()
450437
np.testing.assert_allclose(qc.get_U(), U.ravel() / 4)
451438
np.testing.assert_allclose(qc.get_V(), V.ravel() / 6)
452439

453440

441+
def test_quiver_deprecated_attribute():
442+
fig, ax = plt.subplots()
443+
X = np.arange(-10, 10, 1)
444+
Y = np.arange(-10, 10, 1)
445+
U, V = np.meshgrid(X, Y)
446+
C = np.hypot(U, V)
447+
qc = mquiver.Quiver(
448+
ax, X, Y, U, V, C
449+
)
450+
ax.add_collection(qc)
451+
ax.autoscale_view()
452+
453+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
454+
x = qc.X
455+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
456+
qc.X = x * 2
457+
np.testing.assert_allclose(qc.get_X(), x * 2)
458+
459+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
460+
y = qc.Y
461+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
462+
qc.Y = y * 2
463+
np.testing.assert_allclose(qc.get_Y(), y * 2)
464+
465+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
466+
np.testing.assert_allclose(qc.N, len(qc.get_offsets()))
467+
468+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
469+
u = qc.U
470+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
471+
qc.U = u * 2
472+
np.testing.assert_allclose(qc.get_U(), u * 2)
473+
474+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
475+
v = qc.V
476+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
477+
qc.V = v * 2
478+
np.testing.assert_allclose(qc.get_V(), v * 2)
479+
480+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
481+
xy = qc.XY
482+
with pytest.warns(mpl.MatplotlibDeprecationWarning):
483+
qc.XY = xy * 2
484+
np.testing.assert_allclose(qc.get_offsets(), xy * 2)
485+
486+
454487
def test_quiver_limits():
455488
ax = plt.axes()
456489
x, y = np.arange(8), np.arange(10)

0 commit comments

Comments
 (0)
0