Repository: matplotlib/matplotlib
Pull request: FIX: Don't enable IPython integration if not entering REPL.
IPython can also be used to simply run files; when this is the case we
are not going to be interactive; and we do not want to enable the
eventloop integration. Otherwise someone doing:

    $ ipython script.py

Will simply see a window blink, as IPython will exit immediately instead
of showing the window and exit.

I'm not quite sure how to test that are we basically _want_ something to
block forever and wait for user input; so no test added

Closes: https://github.com/ipython/ipython/issues/11837

## PR Summary

## PR Checklist

- [ ] Has Pytest style unit tests
- [x] Code is [Flake 8](http://flake8.pycqa.org/en/latest/) compliant
- [ ] New features are documented, with examples if plot related
- [ ] Documentation is sphinx and numpydoc compliant
- [ ] Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
- [ ] Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way




Problem statement:
Matplotlib plt.show() not working in IPython 7.5/7.6 on macOS
I have a simple script from a live training course I teach. The script displays a Seaborn barplot in a Matplotlib Figure and has worked for well over a year without any issues. 

If I run the script from a macOS Terminal window with IPython 7.5 or 7.6 (installed from Anaconda), it does not show the Matplotlib Figure containing the barplot. 

If I enter IPython interactive mode and use the `run` command the script works fine and displays the Figure. If I execute the script's code in IPython interactive mode with matplotlib support enabled, the code also works fine.

In addition, this script works when executed from a Terminal window using IPython 7.4.

I have multiple people who attended my Python recent class who are experiencing the same problem--all of them are using various recent macOS versions and have the same issue executing the script with IPython 7.5 or 7.6 at the command line. 

Any recommendations on how to debug this issue?

Hints from issue discussion:
Can you share the script (even privately by email) ?

The 7.4->7.5 changes were really minimal and did not touch the matpltlib integrations so I'm not sure. 7.6 has a but with the `%matplotlib` magic. 


Sure. Where should I send it?
You can send it to me, `bussonniermatthias@gmail.com`.
What Matplotlib backend?  We have been getting reports like this on the Matplotlib side as well related to Tk (which seems to depend on exactly what version of tk is being used) and Qt (where the input hook seems to go off the rails).

What version of Matplotlib and the GUI frameworks?
Matthias, script on the way

Thomas, backend is Qt5Agg on my Mac--assuming this is true for all my Mac-based students. There's no reason they'd even know about changing backends at this point as they're new to Python, Matplotlib and Seaborn.

I had to do a reinstall of Anaconda and all the packages I was using in my Monday Python live training class right in the middle of the day to correct this issue, so the versions I now have installed do not match with what caused these issues. 

At the time, I was running IPython 7.6.1 I believe, now I am on 7.4.0. Most likely, I was running the most up to date matplotlib/seaborn that `conda update --all` would have installed last week.

Students reporting issues are currently on 7.5 and 7.6.1, but I do even have one who says he's on 7.4, can run the code in an interactive IPython session, but cannot get the Matplotlib Figure window to appear when the same code runs as a standalone script. 

The versions and where you installed everything from matters because we have both made Matplotlib do better about auto-selecting a good backend and the packagers have changed what backend the set by default.  There have also been some changes with Matplotlib around how we handle framework/non-framework builds on OSX....

@pdeitel can you drive your system back to a broken state?  Is the brokenness backend dependent?

> If I run the script from a macOS Terminal window with IPython 7.5 or 7.6 (installed from Anaconda), it does not show the Matplotlib Figure containing the barplot.

Does this mean doing 

```
$ ipython file.py
```

from the shell?
OK. I'll have to get my system back into that state, but can't do that until sometime next week--I am teaching a brand new Python DS/AI course on Tuesday and need this to work. 

I've never switched away from the default backend on my Mac, so I believe it was Qt5Agg even when this problem occurred, but I'll figure that out next week. 

Yes, running the script from a Terminal or shell and displaying a standalone window containing the Matplotlib Figure/Axes/Seaborn plot is the issue. I was not having any trouble, for example, in JuputerLab having static visualizations appear inline in a notebook. 

However, I was having an issue getting Tab autocompletion to work both in an interactive IPython 7.6.1 session in my Terminal window and in JuputerLab.
Oh, and generally I use conda to install everything. I use pip only if something is not available via conda. And I don't change the default installation location of anything. I need my environment to match as closely as possible what my students environments will be. Given that many are just learning to program, they would not mess with installation options unless told to do so.
I can get it to not-work on my machine. `ipython script.py arg1 arg2` get the interactive window to open and close immediately. 

if I do `ipython -i script.py arg1 arg2` it wors as expected. So it's likely a ventloop integration stuff. 
```python

import matplotlib.pyplot as plt

print(plt.isinteractive())

fig, ax = plt.subplots()
ax.plot(range(7))

plt.show()

```

I can reproduce this too, `python file.py` works, `ipython file.py` flashes, `ipython -i file.py` works. 

https://github.com/matplotlib/matplotlib/blob/9baab6900f636cae4a204a481a009aa757e9299f/lib/matplotlib/backend_bases.py#L3312-L3345 That is where Matplotlib sorts out what the default blocking behavior of `plt.show()` should be.  

This is apparently a change between Matplotlib 3.0 and 3.1

Commit message: FIX: preserve interactive state around ip.enable_matplotlib()

closes #14950

Patch to review:
diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py
index dfa18b92079f..da4adabfcc37 100644
--- a/lib/matplotlib/backend_bases.py
+++ b/lib/matplotlib/backend_bases.py
@@ -1553,6 +1553,20 @@ def _draw(renderer): raise Done(renderer)
             return figure._cachedRenderer
 
 
+def _is_non_interactive_terminal_ipython(ip):
+    """
+    Return whether we are in a a terminal IPython, but non interactive.
+
+    When in _terminal_ IPython, ip.parent will have and `interact` attribute,
+    if this attribute is False we do not setup eventloop integration as the
+    user will _not_ interact with IPython. In all other case (ZMQKernel, or is
+    interactive), we do.
+    """
+    return (hasattr(ip, 'parent')
+        and (ip.parent is not None)
+        and getattr(ip.parent, 'interact', None) is False)
+
+
 class FigureCanvasBase:
     """
     The canvas the figure renders into.
@@ -1649,9 +1663,18 @@ def _fix_ipython_backend2gui(cls):
             # Work around pylabtools.find_gui_and_backend always reading from
             # rcParamsOrig.
             orig_origbackend = mpl.rcParamsOrig["backend"]
+
             try:
                 mpl.rcParamsOrig["backend"] = mpl.rcParams["backend"]
-                ip.enable_matplotlib()
+                if _is_non_interactive_terminal_ipython(ip):
+                    pass
+                else:
+                    was_interacive = mpl.is_interactive()
+                    try:
+                        ip.enable_matplotlib()
+                    finally:
+                        mpl.interactive(was_interacive)
+
             finally:
                 mpl.rcParamsOrig["backend"] = orig_origbackend
 


Code review comments:
diff_hunk: @@ -1649,9 +1663,18 @@ def _fix_ipython_backend2gui(cls):
             # Work around pylabtools.find_gui_and_backend always reading from
             # rcParamsOrig.
             orig_origbackend = mpl.rcParamsOrig["backend"]
+
             try:
                 mpl.rcParamsOrig["backend"] = mpl.rcParams["backend"]
-                ip.enable_matplotlib()
+                if _is_non_interactive_terminal_ipython(ip):
+                    pass
+                else:
+                    was_interacive = mpl.is_interactive()
path: lib/matplotlib/backend_bases.py
text: @user1:
typo interactive