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

Skip to content

Shorten various offsetbox implementations #21699

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

Closed
wants to merge 3 commits into from

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Nov 21, 2021

PR Summary

1st commit: Inherit OffsetBox.{get_offset,get_window_extent}.

By making the parameters of OffsetBox.get_offset optional (which matches
the fact that there are currently subclasses for which get_offset does
not take any parameters), and adjusting OffsetBox.get_window_extent to
use get_extent (which is typically redefined in subclasses) instead of
get_extent_offsets (which is not), we can inherit the implementation of
OffsetBox.get_window_extent across most subclasses instead of having to
copy-paste it again and again.

2nd commit: Inline AnchoredOffsetBox._update_offset_func.

It is only called in get_window_extent -- and in draw, but that
call is immediately followed by a call to get_window_extent, so it is
redundant.

PR Checklist

Tests and Styling

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (install flake8-docstrings and run flake8 --docstring-convention=all).

Documentation

  • New features are documented, with examples if plot related.
  • New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

IMHO this change would make get_offset() basically unusable because users can't know whether they have to pass any parameters. We can't have that on public API.

@@ -272,23 +272,15 @@ def offset(width, height, xdescent, ydescent, renderer) \
self._offset = xy
self.stale = True

def get_offset(self, width, height, xdescent, ydescent, renderer):
def get_offset(self, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

This is deeply confusing. It's almost impossible for the user to figure out whether he has to pass anything and if so what. We can't have that on public API. At the very least, you should make the parameters explicit

Suggested change
def get_offset(self, *args, **kwargs):
def get_offset(self, width=None, height=None, xdescent=None, ydescent=None, renderer=None):

But even with that I'm very concerned with the API. "The method needs arguments depending on the subclass and/or some configurable state." is not a contract you can program against. It's bad enough that we currently have the inconsistency that some subclasses do not take arguments. But at least that's explicit and when you know the class, you know what to pass. This change here would make that more implicit.

Copy link
Member

Choose a reason for hiding this comment

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

I still propose to leave the parameters explicit until they are removed completely.

Copy link
Member

Choose a reason for hiding this comment

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

The way the API currently is is just as bad though. In some cases you have to pass arguments .... just for us to drop them on the floor!

I think that this is going the right direction (no inputs anywhere), but I think I agree we need more steps to get there.

@anntzer
Copy link
Contributor Author

anntzer commented Feb 13, 2022

I agree the API is pretty confusing, but that's what it was before. Still, it can be improved (last commit now) by making the callables passed to set_offset take no arguments whatsoever (they can call get_extent themselves), and therefore make get_offset also take no arguments. For backcompat I maintained the ability to pass *args, **kwargs if the callable passed to set_offset likewise takes such parameters (and likewise maintained in the AnchoredOffsetbox offset computer), but this can be deprecated in a later step.

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Removing all parameters from get_offset() seems like a good way forward. Maybe we should even deprecate right now to give more direction and not have to word ourselves through what can or should be passed.

@@ -272,23 +272,15 @@ def offset(width, height, xdescent, ydescent, renderer) \
self._offset = xy
self.stale = True

def get_offset(self, width, height, xdescent, ydescent, renderer):
def get_offset(self, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

I still propose to leave the parameters explicit until they are removed completely.

@anntzer
Copy link
Contributor Author

anntzer commented Feb 13, 2022

Making args to get_offset explicit makes everything much more complicated, because now I need to check the signature of self._offset (if it is a callable) to decide whether to pass args or not (even if you make the signature width=None, ... you cannot know whether the called did get_offset() or get_offset(None, ...) (which could be technically valid with whatever offset func they set previously).
Likewise, making the docstring of set_offset more restrictive actually seems more confusing, as you'd now need to explicitly state the "old" signature which is something we want to get rid of.
I have no qualms with deprecating the old set_offset form but again don't think it has to be done as part of this PR (which breaks no backcompat whatsoever, AFAICT).

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

Having get_offset callables without parameters might be an improvement. - I'm not even too sure whether these changes are worth it.
But the API is getting really messy and we need to thoroughly document it if we want to go that direction.

@timhoffm timhoffm dismissed their stale review March 6, 2022 21:52

If properly documented

@anntzer
Copy link
Contributor Author

anntzer commented Apr 11, 2022

Done (with minor rewordings on top of the proposed docs).

@anntzer anntzer force-pushed the inlineoffset branch 3 times, most recently from 4af7bb0 to 6045113 Compare April 12, 2022 07:52
@QuLogic
Copy link
Member

QuLogic commented Apr 14, 2022

@tacaswell has told me that the message makes sense from the contents, but that last commit message is a bit inscrutable at first glance.

anntzer added 3 commits April 14, 2022 12:18
By making the parameters of OffsetBox.get_offset optional (which matches
the fact that there are currently subclasses for which get_offset does
*not* take any parameters), and adjusting OffsetBox.get_window_extent to
use get_extent (which is typically redefined in subclasses) instead of
get_extent_offsets (which is not), we can inherit the implementation of
OffsetBox.get_window_extent across most subclasses instead of having to
copy-paste it again and again.
It is only called in `get_window_extent` -- and in `draw`, but *that*
call is immediately followed by a call to `get_window_extent`, so it is
redundant.
@anntzer
Copy link
Contributor Author

anntzer commented Apr 14, 2022

Agreed, is the new title better? :)

@@ -263,32 +263,35 @@ def set_offset(self, xy):
xy : (float, float) or callable
The (x, y) coordinates of the offset in display units. These can
either be given explicitly as a tuple (x, y), or by providing a
function that converts the extent into the offset. This function
must have the signature::
function that dynamically computes an offset (taking the arguments
Copy link
Member

Choose a reason for hiding this comment

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

This needs an API change note.

@@ -357,7 +362,7 @@ def draw(self, renderer):
to the given *renderer*.
"""
w, h, xdescent, ydescent, offsets = self.get_extent_offsets(renderer)
px, py = self.get_offset(w, h, xdescent, ydescent, renderer)
Copy link
Member

Choose a reason for hiding this comment

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

Any user who has used set_offset with a callable will be broken by this (as they no longer get the inputs they expect).

@tacaswell
Copy link
Member

This behavior / signature goes back to 3ae9221 which is related to mouse-draggable artists.

I do not feel I understand why it was there to begin with to be confident ripping it out.

@jklymak jklymak marked this pull request as draft June 2, 2022 12:29
@jklymak
Copy link
Member

jklymak commented Jun 2, 2022

Moved to draft. Seems to at least need an Api note, and response to comments above

@anntzer
Copy link
Contributor Author

anntzer commented Nov 1, 2022

Superseded by #23896 and #23907.

@anntzer anntzer closed this Nov 1, 2022
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.

7 participants