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

Skip to content

Simplify FigureCanvas multiple inheritance init by swapping bases order. #23363

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 1 commit into from
Jun 29, 2022

Conversation

anntzer
Copy link
Contributor

@anntzer anntzer commented Jun 28, 2022

Instead of having e.g. FigureCanvasQT inherit from QWidget and then
FigureCanvasBase, which requires workarounds to handle the fact that
PySide.QWidget does not participate in cooperative multiple inheritance
(we work around that with _allow_super_init in qt, and manual super
calls in other toolkits), just swap the order of bases to
(FigureCanvasBase, QWidget) and make sure that FigureCanvasBase does
participate in cooperative multiple inheritance.

Also note the changes in FigureCanvasGTK{3,4}Agg.draw, where the new
inheritance order also makes more sense.

PR Summary

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).

Instead of having e.g. FigureCanvasQT inherit from QWidget and then
FigureCanvasBase, which requires workarounds to handle the fact that
PySide.QWidget does not participate in cooperative multiple inheritance
(we work around that with _allow_super_init in qt, and manual super
calls in other toolkits), just swap the order of bases to
(FigureCanvasBase, QWidget) and make sure that FigureCanvasBase *does*
participate in cooperative multiple inheritance.

Also note the changes in FigureCanvasGTK{3,4}Agg.draw, where the new
inheritance order also makes more sense.
@tacaswell tacaswell added this to the v3.6.0 milestone Jun 28, 2022
@tacaswell
Copy link
Member

Given the simplicity of this solution, why did we systematically have every class defined in a way that was as difficult as possible?

@@ -1633,6 +1633,7 @@ def __init__(self, figure=None):
# We don't want to scale up the figure DPI more than once.
figure._original_dpi = figure.dpi
self._device_pixel_ratio = 1
super().__init__() # Typically the GUI widget init (if any).
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to pass *args, **kwargs through here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FigureCanvasBase.__init__ itself doesn't take *args, **kwargs; let's not bother adding that unless there's a real reason to do so.

Copy link
Member

Choose a reason for hiding this comment

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

I am weakly in favor of atleast **kwargs flowing through this init, but also fine with waiting until it actually is a need someone has.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think passing **kwargs through only makes sense if figure was kwonly as well (so that at least in theory, each class in the inheritance tree can pick out whatever kwargs they support). Once you start having positional arguments, the scheme breaks down anyways (as you can't tell which positional arguments you need to pick out).

@anntzer
Copy link
Contributor Author

anntzer commented Jun 28, 2022

Actually some classes are already set up the other(=new) way round (tkagg, tkcairo, wxagg); my (completely uneducated) guess is that this was probably random at the beginning (especially when people were not using super() but rather manually calling the superclasses, so the MRO didn't matter as much) and that we just went for the complicated workaround when the problem with pyside2 MI showed up.

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

Mildly in favor of FigureCanvasBase.__init__ passing **kwargs through to the super call but OK with this as-is.

@timhoffm timhoffm merged commit ca9a779 into matplotlib:main Jun 29, 2022
@anntzer anntzer deleted the coop-mi branch June 29, 2022 15:44
@NeilGirdhar
Copy link

PySide.QWidget does not participate in cooperative multiple inheritance

Wow. Did you file a bug with PySide?

@tacaswell
Copy link
Member

tacaswell commented Jul 2, 2022

Wow. Did you file a bug with PySide?

No, I am 99.9% sure they are aware of this and have maintained the non-cooperative inheritance as an intentional design choice. For users there can be weird breakage when you switch to using super(). The choice to prioritize making sure the upgrade path within the PySide family is as easy as possible over API consistency with PyQt* or Python's notion of super is a very reasonable one.

[edited to add missing words 🤦🏻 ]

@NeilGirdhar
Copy link

NeilGirdhar commented Jul 3, 2022

No, I am 99.9% sure they are aware of this and have maintained the non-cooperative inheritance as an intentional design choice.

That's really unfortunate.

The choice to prioritize making sure the upgrade path within the PySide family is as easy as possible over API consistency with PyQt* or Python's notion of super is a very reasonable one.

I understand your point. But they could at least add a warning in __init__:

class QWidget:
  def __init__(self, ..., **kwargs: Any):
    if super().__init__ is not object.__init__:
      warnings.warn("Currently, QWidget doesn't participate in cooperative inheritance.  However, in a future "
                    f"version of PySide, QWidget will call {super().__init__} "
                    f"with parameters {kwargs} "
                    f"for objects of type {type(self).__name__}.", DeprecationWarning)

That way in a year or two, they can call super as they are "supposed to", and everyone is happy.

@tacaswell
Copy link
Member

@NeilGirdhar If you want to push on this I suggest you propose it upsteam with with Pyside (discussing it here is not going to change anything).

While I agree it would be nicer if they did participate in cooperative MI, I do not think not doing so is wrong and that is not a windmill I want to tilt at right now ;) .

@NeilGirdhar
Copy link

@tacaswell Fair enough. At least you found a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants