Skip to content

Further defer backend selection #22005

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 7 commits into from
Mar 3, 2022

Conversation

tacaswell
Copy link
Member

PR Summary

This PR does two things (that maybe should be broken up)

  1. delays the first call to switch backend until absolutely needed (which is when I read [Bug]: Working with PyQt5, the different import order will make different result. #21998 too fast is what I thought the actual bug was (because I ran into that bug in my BNL work this week)
  2. makes importing backend_qt5* use a Qt5 binding even if a Qt6 binding is installed.

In both cases the underlying bug is that if a Qt6 binding is installed there is no way (other than pre-importing it!) to force us to use the Qt5 bindings (in my case it was getting hit by the automatic backend resolution on pyplot import and in the linked issue due to a direct import of backend_qt5!).

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](https://matplotlib.org/devel/documenting_mpl.html#building-the-docs) without error).

@tacaswell tacaswell added this to the v3.5.2 milestone Dec 19, 2021
@tacaswell tacaswell force-pushed the further_defer_backend_selection branch from b57b94e to 95a2566 Compare December 22, 2021 21:24
@tacaswell
Copy link
Member Author

It helps to get the polarity of the test correct on filtering.

@tacaswell
Copy link
Member Author

My guess on the failing tk tests is that because the tk modules are no longer eagerly imported on pyplot import there is something missing with our internal module. However I do not understand why it works on linux and appveyor.

@tacaswell tacaswell force-pushed the further_defer_backend_selection branch 2 times, most recently from 729d021 to e6a9e4d Compare December 31, 2021 03:22
@tacaswell
Copy link
Member Author

Do we know how to get coverage data on the code we ship off to sub-processes this way?

Is this too big for a bug-fix?

@anntzer
Copy link
Contributor

anntzer commented Jan 2, 2022

In theory things should be automatically covered (the other tests contribute to coverage).
(More specifically, the source of the test itself doesn't contribute, but I can live with that.)

@tacaswell
Copy link
Member Author

I'm worried about the coverage on the test code (which @dopplershift has made the compelling case should always be ~100%).

@dopplershift
Copy link
Contributor

You can configure coverage.py to run coverage for subprocess.

And while I never get matplotlib's suite to 100% lines of code for tests (because we have some non-deterministic behavior e.g. repeatedly running stuff until no error), it really is a nice check to have. There's no reason to have test code that's not run.

@tacaswell
Copy link
Member Author

But we are not running subprocces on a file, we are running it on a bunch of code that we (maybe too cleverly) read out of a function we define inline and then run with python -c .....

I guess the right thing to do there make a new directory and toss off the the inline stuff there. It is a pity to lose the readability win of the actual test-code being in-line for the tooling, but not having the computer tell us if we have coverage on these lines is a bit worrying (as it would be easy to break the subprocess tests in some way and never have them run and we would not know).

@dopplershift
Copy link
Contributor

I'm not following why running with python -c prohibits doing the steps that the coverage docs outline:

  1. Set COVERAGE_PROCESS_START
  2. Run process_startup, like:
import coverage
coverage.process_startup()

@dopplershift
Copy link
Contributor

Doh, I get it now. The lines can't be matched up to the original lines of code. Hrmm...

@tacaswell
Copy link
Member Author

I see how to write the test for the warning, but out of time right now and want some feedback on if this is actually useful.

The formatted warning looks like:

UserWarning: Matplotlib is using PyQt6 which wraps 6.2.2 however an instantiated QApplication from PyQt5 which wraps 5.15.2 exists.  Mixing Qt major versions may not work as expected.

@tacaswell
Copy link
Member Author

At least locally, I am semi-magically seeing coverage from the sub-process without doing anything extra. I assume this is due to pytest-cov being helpful?

This PR got a bit out of control and I ended up tweaking the tk tests, but we should be getting coverage on them now as well!

I still need to finish writing the cross Qt major version test.

@tacaswell
Copy link
Member Author

I probably should squash this to a smaller number of commits, but I think this is otherwise good to go!

@tacaswell tacaswell force-pushed the further_defer_backend_selection branch 5 times, most recently from ee7d467 to 4a2f554 Compare January 15, 2022 04:00
Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

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

Do you need to explicitly empty/unset QT_API in tests?

if QT_API_ENV in ["pyqt5", "pyside2"]:
QT_API = _ETS[QT_API_ENV]
else:
_QT_MODE = 5 # noqa
Copy link
Member

Choose a reason for hiding this comment

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

Why does this need noqa?

Copy link
Member Author

Choose a reason for hiding this comment

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

It will otherwise complain about overwriting an unused import.

@QuLogic QuLogic mentioned this pull request Feb 2, 2022
4 tasks
@tacaswell tacaswell force-pushed the further_defer_backend_selection branch from d3f83b3 to bbbe8d2 Compare February 3, 2022 00:15
@tacaswell tacaswell force-pushed the further_defer_backend_selection branch from ba24b8b to 3af4be1 Compare February 10, 2022 04:26
tacaswell and others added 2 commits March 3, 2022 17:08
This is to prevent the early importing of GUI bindings in the case where the
user wants to use something later in our search list, but also has a toolkit
higher in our list installed which we select.

Thanks to @anntzer for the implementation suggestion.
Because the code in qt_compat tries qt6 bindings first, backend_qt supports
both Qt5 and Qt6, and the qt5 named backends are shims to the generic Qt
backend, if you imported matplotlib.backends.backend_qt5agg,
matplotlib.backends.backend_qt5cairo, or matplotlib.backends.backend_qt5, and

1. had PyQt6 or pyside6 installed
2. had not previously imported a Qt5 binding

Then you will end up with a backend that (by name) claims to be Qt5, but will
be using Qt6 bindings.  If you then subsequently import a Qt6 binding and try
to embed the canvas it will fail (due to being Qt6 objects not Qt5 objects!).

Additional changes to qt_compat that only matters if

1. rcparams['backend'] is set to qt5agg or qt5agg
2. QT_API env is not set
3. the user directly import matplotlib.backends.qt_compat

This will likely only affect users who are using Matplotlib as an
qt-shim implementation.

closes matplotlib#21998

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
@tacaswell tacaswell force-pushed the further_defer_backend_selection branch from 66b03ad to efc7f81 Compare March 3, 2022 22:08
tacaswell and others added 3 commits March 3, 2022 17:08
By putting the implementation in top-level functions and then importing the
test module in the sub-process we are able to get accurate coverage on these
tests.

pytest-cov takes care of all of the coverage related magic implicitly.

Also get coverage information out of isolated tk tests.

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
@QuLogic QuLogic merged commit daaa1ed into matplotlib:main Mar 3, 2022
@lumberbot-app
Copy link

lumberbot-app bot commented Mar 3, 2022

Owee, I'm MrMeeseeks, Look at me.

There seem to be a conflict, please backport manually. Here are approximate instructions:

  1. Checkout backport branch and update it.
git checkout v3.5.x
git pull
  1. Cherry pick the first parent branch of the this PR on top of the older branch:
git cherry-pick -m1 daaa1ed376b4fc60ed5a20d155a13c6361aee479
  1. You will likely have some merge/cherry-pick conflict here, fix them and commit:
git commit -am 'Backport PR #22005: Further defer backend selection'
  1. Push to a named branch:
git push YOURFORK v3.5.x:auto-backport-of-pr-22005-on-v3.5.x
  1. Create a PR against branch v3.5.x, I would have named this PR:

"Backport PR #22005 on branch v3.5.x (Further defer backend selection)"

And apply the correct labels and milestones.

Congratulations — you did some good work! Hopefully your backport PR will be tested by the continuous integration and merged soon!

Remember to remove the Still Needs Manual Backport label once the PR gets merged.

If these instructions are inaccurate, feel free to suggest an improvement.

@tacaswell tacaswell deleted the further_defer_backend_selection branch March 3, 2022 23:22
@tacaswell
Copy link
Member Author

Sigh, I'll do the backport.

tacaswell pushed a commit to tacaswell/matplotlib that referenced this pull request Mar 4, 2022
Merge pull request matplotlib#22005 from tacaswell/further_defer_backend_selection

Further defer backend selection
tacaswell pushed a commit to tacaswell/matplotlib that referenced this pull request Mar 4, 2022
Merge pull request matplotlib#22005 from tacaswell/further_defer_backend_selection

Further defer backend selection
QuLogic added a commit that referenced this pull request Mar 5, 2022
…-v3.5.x

Backport PR #22005: Further defer backend selection
tacaswell added a commit to tacaswell/matplotlib that referenced this pull request May 17, 2022
closes matplotlib#23042

In matplotlib#22005 we change `pyplot` so that at import time we do not force a switch of
the backend and install the repl displayhook.

However, this meant that in some cases (primarily through `ipython --pylab`) to
end up with a session where `install_repl_displayhook` had never been called.
tacaswell added a commit to tacaswell/matplotlib that referenced this pull request May 17, 2022
closes matplotlib#23042

In matplotlib#22005 we change `pyplot` so that at import time we do not force a switch of
the backend and install the repl displayhook.

However, this meant that in some cases (primarily through `ipython --pylab`) to
end up with a session where `install_repl_displayhook` had never been called.
tacaswell added a commit to tacaswell/matplotlib that referenced this pull request May 17, 2022
closes matplotlib#23042

In matplotlib#22005 we change `pyplot` so that at import time we do not force a switch of
the backend and install the repl displayhook.

However, this meant that in some cases (primarily through `ipython --pylab`) to
end up with a session where `install_repl_displayhook` had never been called.
tacaswell added a commit to tacaswell/matplotlib that referenced this pull request May 18, 2022
closes matplotlib#23042

In matplotlib#22005 we change `pyplot` so that at import time we do not force a switch of
the backend and install the repl displayhook.

However, this meant that in some cases (primarily through `ipython --pylab`) to
end up with a session where `install_repl_displayhook` had never been called.

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
andrew-fennell pushed a commit to andrew-fennell/matplotlib that referenced this pull request Jun 14, 2022
closes matplotlib#23042

In matplotlib#22005 we change `pyplot` so that at import time we do not force a switch of
the backend and install the repl displayhook.

However, this meant that in some cases (primarily through `ipython --pylab`) to
end up with a session where `install_repl_displayhook` had never been called.

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
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.

5 participants