Repository: napari/napari

Issue:
working on fixing tests
## 🐛 Bug

here are some crazy observations that started from looking into the test failure in #923, and may have some implications for general test fixes that we want to look into in #921.  I'll patch it in a PR soon, but just want to document this somewhere, in case it becomes pertinent to #921

on a mac (but we observe it in linux tests too), with python 3.7, using pyqt5 (I'm on 5.14.1), running this code:
```python
import numpy as np
import pytest  # <-- this unused import is required for the bug!
import napari

with napari.gui_qt():
    data = np.random.random((10, 15, 20))
    viewer = napari.view_image(data)
    viewer = napari.view_image(data)
    viewer.dims.ndisplay = 3
    viewer.dims.ndisplay = 2
```
leads to an error deep in vispy that culminates in:
```
lib/python3.7/site-packages/vispy/gloo/glir.py in _validate(self)
   1299                                         gl.GL_VALIDATE_STATUS):
   1300             raise RuntimeError('Program validation error:\n%s'
-> 1301                                % gl.glGetProgramInfoLog(self._handle))
   1302 
   1303     def _post_draw(self):

RuntimeError: Program validation error:
```
observations:
- only happens with python 3.7; python 3.6 & 3.8 are fine
- only happens with pyqt5, not pyside2
- only happens with the **double** viewer instantiation (two calls to `view_image`), assigning both viewers to the same variable name.  If you name the second viewer `viewer2`, it's fine
- only happens when you switch from `ndisplay=` 2, then to 3, back to 2
- the error occurs only once per session.  running the same code a second time works fine.
- pytest is key.  if you comment out the `import pytest` line, you don't get the error...
- i thought it might have something to do with our adding of 'viewer' to console globals... but removing the `self.console = QtConsole({'viewer': self.viewer})` from our `QtConsole` instantiation doesn't change anything.



Discussion:
another note... the following will create an error if you run it in an ipython session started with 
`--gui=qt` (But not if you run it in a `gui_qt` context):

```python
import numpy as np
import pytest  # (this is required for the bug)
import napari

data = np.random.random((10, 15, 20))
viewer = napari.view_image(data)
viewer = napari.view_image(data)  # they must have the same name
viewer.dims.ndisplay = 3
```

... and then all hell breaks loose culminating in `RuntimeError: OpenGL got errors (Check before draw): GL_INVALID_OPERATION`
This sounds crazy @tlambert03! Amazing job chasing this down and good to document here
the two offending modules within pytest are `_pytest.main` and `_ptest.python`
if I comment out all imports from both of those modules, then the error doesn't occur... (though a single import from either of those modules is sufficient to cause this bug) still need to look into those modules to see why
this subtle modification to the first example exits with a full segmentation fault.
(note the shifted position of the second `napari.view_image` call)
 
```python
import numpy as np
import pytest
import napari

with napari.gui_qt():
    data = np.random.random((10, 15, 20))
    viewer = napari.view_image(data)
    viewer.dims.ndisplay = 3
    viewer = napari.view_image(data)
    viewer.dims.ndisplay = 2
```
crazy. is it worth trying to create a minimal vispy example?
yeah, trying to do that.  the strangest part is that pytest has to be imported !?
ooo.  a very nice piece of the puzzle:  the problem is unique to vispy 0.6.4
both of the code segments above run fine on vispy <= 0.6.3
it just keeps getting more interesting!  Turns out the only difference that matters between vispy 0.6.3 and 0.6.4 is simply the [moving of this import](https://github.com/vispy/vispy/commit/eafa62bac096372427ae0c0ad5790a4a42299c8f): `from skimage.measure import find_contours` in [`visuals.isocurve`](https://github.com/vispy/vispy/blob/14bf3023f004e096e4131c1331423a5f43be9883/vispy/visuals/isocurve.py)!

I dug for quite a while through the import chain and eventually tracked it down to something (still not sure quite what) in `scipy.stats` (and i think it's in `scipy.stats.stats`... but that's where the trail ran cold) that needs to be imported before _something_ (not quite sure what) happens when coming back from viewing a volume (`ndisplay==3`) to displaying a 2D image.  amazingly, adding that single import fixes it:

```python
import numpy as np
import pytest
import napari

with napari.gui_qt():
    data = np.random.random((10, 15, 20))
    viewer = napari.view_image(data)
    viewer = napari.view_image(data)
    viewer.dims.ndisplay = 3
    from scipy import stats  # <-- wtf. this fixes it!
    viewer.dims.ndisplay = 2
```
For what it's worth, the latest scipy also caused some *very* weird issues in CaImAn, a known calcium source extraction library. See for example https://github.com/flatironinstitute/CaImAn/issues/696 which was resolved by this commit https://github.com/flatironinstitute/CaImAn/commit/5f2193077bf29a208dfa16b4579650339bb829aa. 

Something funky is definitely going on with scipy imports in its latest versions.
just tested and this bug happens with scipy versions as far back as 1.1.0 (and importing scipy.stats still fixes it back then).  so it doesn't seem to be related to any of the most recent scipy stuff
Wow!

Resolving patch:
diff --git a/napari/__init__.py b/napari/__init__.py
--- a/napari/__init__.py
+++ b/napari/__init__.py
@@ -69,13 +69,5 @@
 # register napari object types with magicgui if it is installed
 _magicgui.register_types_with_magicgui()
 
-
-# this unused import is here to fix a very strange bug.
-# there is some mysterious magical goodness in scipy stats that needs
-# to be imported early.
-# see: https://github.com/napari/napari/issues/925
-from scipy import stats  # noqa: F401
-
 del _magicgui
-del stats
 del _viewer_key_bindings


Test patch: