Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Make QuadMesh arguments with defaults keyword_only #20237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 25, 2021

Conversation

timhoffm
Copy link
Member

PR Summary

This is in preparation of changing the signature from

__init__(self, meshWidth, meshHeight, coordinates, antialiased=True, shading='flat', **kwargs)

to

__init__(self, coordinates, *, antialiased=True, shading='flat', **kwargs)

See also: #18472 (comment)

If we want to make this according to deprecation rules this is a multi-step process:

  • Make everything after coordinates keyword-only. (This PR).
  • When that deprecation expires (in 3.7), change to
    __init__(self, *args, antialiased=True, shading='flat', **kwargs) and extract meshWidth, meshHeight, coordinates from args, kwargs. At the same time, deprecate passing meshWidth, meshHeight, 1d_coordinates in favor of passing 2D coordinates only.
  • When that deprecation expires (in 3.9), change back to __init__(self, coordinates, *, antialiased=True, shading='flat', **kwargs)

The in between *args step is essential for a smooth transition phase allowing both signatures width, height, 1d_coordinates and 2d_coordinates.

Possible shortcut: Skip the first step and directly go to the *args signature. This would be a breaking change, but only for people, who pass antialiased as positional argument.

@timhoffm timhoffm added this to the v3.5.0 milestone May 15, 2021
@greglucas
Copy link
Contributor

Nice, I think you are much more lenient than I was going to be. I had this following diff ready to go, which would be your second choice. I'd still vote for skipping the keyword-only deprecation period as I can't imagine many people relying on that "feature".

diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py
index d02607cc5..bcce49440 100644
--- a/lib/matplotlib/collections.py
+++ b/lib/matplotlib/collections.py
@@ -1977,34 +1977,38 @@ class QuadMesh(Collection):
     (*meshWidth* * *meshHeight*) quadrilaterals in the mesh.  The mesh
     need not be regular and the polygons need not be convex.
 
-    A quadrilateral mesh is represented by a (2 x ((*meshWidth* + 1) *
-    (*meshHeight* + 1))) numpy array *coordinates*, where each row is
-    the *x* and *y* coordinates of one of the vertices.  To define the
+    A quadrilateral mesh is represented by a ((*meshWidth* + 1),
+    (*meshHeight* + 1), 2) numpy array *coordinates*, where the final dimension
+    is the *x* and *y* coordinates of one of the vertices.  To define the
     function that maps from a data point to its corresponding color,
-    use the :meth:`set_cmap` method.  Each of these arrays is indexed in
-    row-major order by the mesh coordinates of the vertex (or the mesh
-    coordinates of the lower left vertex, in the case of the colors).
-
-    For example, the first entry in *coordinates* is the coordinates of the
-    vertex at mesh coordinates (0, 0), then the one at (0, 1), then at (0, 2)
-    .. (0, meshWidth), (1, 0), (1, 1), and so on.
+    use the :meth:`set_cmap` method.
 
     *shading* may be 'flat', or 'gouraud'
     """
-    def __init__(self, meshWidth, meshHeight, coordinates,
+    def __init__(self, coordinates, *args,
                  antialiased=True, shading='flat', **kwargs):
         super().__init__(**kwargs)
-        self._meshWidth = meshWidth
-        self._meshHeight = meshHeight
-        # By converting to floats now, we can avoid that on every draw.
-        self._coordinates = np.asarray(coordinates, float).reshape(
-            (meshHeight + 1, meshWidth + 1, 2))
+        if len(args):
+            _api.warn_deprecated("3.5", alternative=(
+                "The QuadMesh class now takes a 3-dimensional *coordinates* "
+                "array as input of shape (nx, ny, 2) that defines the shape "
+                "and size of the mesh."))
+            self._meshWidth = coordinates
+            self._meshHeight = args[0]
+            # By converting to floats now, we can avoid that on every draw.
+            self._coordinates = np.asarray(args[1], float).reshape(
+                (self._meshHeight + 1, self._meshWidth + 1, 2))
+        else:
+            # This will become standard after the deprecation
+            self._coordiantes = np.asarray(coordinates, float)
+            self._meshWidth = self._coordinates.shape[0] - 1
+            self._meshHeight = self._coordinates.shape[1] - 1
+
         self._antialiased = antialiased
         self._shading = shading
 
         self._bbox = transforms.Bbox.unit()
-        self._bbox.update_from_data_xy(coordinates.reshape(
-            ((meshWidth + 1) * (meshHeight + 1), 2)))
+        self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2))
 
     def get_paths(self):
         if self._paths is None:

@jklymak
Copy link
Member

jklymak commented May 20, 2021

I'd personally vote for the simpler solution. I expect vanishingly few users use QuadMesh directly, and I doubt any developers who use it are going to be using the kwargs positionally.

@anntzer
Copy link
Contributor

anntzer commented May 20, 2021

I think this can be done by manually writing the deprecation instead? Something like

def __init__(self, *args, **kwargs):
    old_sig = inspect.signature(lambda self, meshWidth, ...)
    new_sig = inspect.signature(lambda self, coordinates, ...)
    try:
        bound = new_sig.bind(*args, **kwargs)
        # extract arguments, do the actual constructor work
        ...
    except TypeError as exc:
        try:
            bound = old_sig.bind(*args, **kwargs)
            # user passed in args using the old call convention
            warn_deprecated(...)
            # extract arguments, do the actual constructor work
            ...
        except TypeError:
            raise exc  # probably the new_sig binding error makes more sense

# so that docs and introspection display the new signature
__init__.__signature__ = inspect.signature(
    lambda self, coordinates, *, antialiased=True, ...)

taking advantage of the fact that we can distinguish an old-style call from a new-style call just from the passed in args? (even ignoring arg types, I don't think(?) a call can ever be correct for both signatures)

@timhoffm
Copy link
Member Author

A major part of this PR does not come from the deprecation logic itself. It's rather adapting our internal calls to the new API and tests that the old API still works and produces the same result as the new API.

@timhoffm timhoffm force-pushed the quadmesh-init branch 2 times, most recently from c811d1c to 1f5ad17 Compare May 23, 2021 22:55
Copy link
Contributor

@anntzer anntzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can selfmerge after fixing typos

@timhoffm timhoffm merged commit c56e3c5 into matplotlib:master May 25, 2021
@timhoffm timhoffm deleted the quadmesh-init branch May 25, 2021 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants