internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: vispy/vispy

Issue:
Python 3.8 RuntimeError: dictionary keys changed during iteration
`windows`, `vispy 0.6.1`, `x64`

When I have installed python 3.7, vispy 0.6.1 was built successfully with minimal Microsoft Visual C++ 14.0 Build Tools during pip install. And my application ran great.

Now I have python 3.8, and vispy pip installation process fails with the errors. OK, I downloaded .whl from https://www.lfd.uci.edu/~gohlke/pythonlibs and installed successfully. Then I started my app and it crashed with the following:

```
  File "C:\Python38\lib\site-packages\vispy\scene\canvas.py", line 134, in __init__
    super(SceneCanvas, self).__init__(
  File "C:\Python38\lib\site-packages\vispy\app\canvas.py", line 194, in __init__
    self._set_keys(keys)
  File "C:\Python38\lib\site-packages\vispy\app\canvas.py", line 249, in _set_keys
    for key, val in keys.items():
RuntimeError: dictionary keys changed during iteration
```


Discussion:
Looks like you're right. This should probably be updated to create a new dictionary. Pull requests welcome!
Wow, I'm afraid, I have no enough qualification for pull request... although might I will try later. For now I solved the problem in easy mode.

My app called `scene.SceneCanvas.__init__` with `keys='interactive'` and if I remove this argument, it's works as fine as before.
Let me know if you need any help (github has some nice guides somewhere). Otherwise, I've marked this as "hacktoberfest" so anyone needing some last minute pull requests to get their t-shirt will find this more easily.

Resolving patch:
diff --git a/vispy/app/canvas.py b/vispy/app/canvas.py
--- a/vispy/app/canvas.py
+++ b/vispy/app/canvas.py
@@ -245,6 +245,7 @@ def toggle_fs():
         if not isinstance(keys, dict):
             raise TypeError('keys must be a dict, str, or None')
         if len(keys) > 0:
+            lower_keys = {}
             # ensure all are callable
             for key, val in keys.items():
                 if isinstance(val, string_types):
@@ -256,9 +257,8 @@ def toggle_fs():
                 if not hasattr(val, '__call__'):
                     raise TypeError('Entry for key %s is not callable' % key)
                 # convert to lower-case representation
-                keys.pop(key)
-                keys[key.lower()] = val
-            self._keys_check = keys
+                lower_keys[key.lower()] = val
+            self._keys_check = lower_keys
 
             def keys_check(event):
                 if event.key is not None:


Test patch:
Repository: vispy/vispy

Issue:
Fix simple typo: withour -> without
There is a small typo in vispy/app/backends/_ipynb_vnc.py.
Should read without rather than withour.




Discussion:

Resolving patch:
diff --git a/vispy/app/backends/_ipynb_vnc.py b/vispy/app/backends/_ipynb_vnc.py
--- a/vispy/app/backends/_ipynb_vnc.py
+++ b/vispy/app/backends/_ipynb_vnc.py
@@ -52,7 +52,7 @@ def _set_config(c):
     _app.backend_module._set_config(c)
 
 
-# Init dummy objects needed to import this module withour errors.
+# Init dummy objects needed to import this module without errors.
 # These are all overwritten with imports from IPython (on success)
 DOMWidget = object
 Unicode = Int = Float = Bool = lambda *args, **kwargs: None


Test patch:
Repository: vispy/vispy

Issue:
Initialize ColorArray from list of color fail 
When initialising `CollorArray` from list of `Colors` it fails. It works if on list there is at least one definition color with string.

This works:
```python
array1 = ColorArray(["b", "w"])
array2 = ColorArray(["black", "white"])
array3 = ColorArray(["black", Color((1, 1, 1))])
```

This fails:
```python
array4 = ColorArray([Color((0,0,0)), Color((1, 1, 1))])
```
with:
```
TypeError: float() argument must be a string or a number, not 'ColorArray'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/czaki/Projekty/partseg/vispy_test.py", line 6, in <module>
    array4 = ColorArray([Color((0,0,0)), Color((1, 1, 1))])
  File "/home/czaki/.pyenv/versions/partseg3.6-pyside/lib/python3.6/site-packages/vispy/color/color_array.py", line 148, in __init__
    rgba = _user_to_rgba(color, clip=clip)
  File "/home/czaki/.pyenv/versions/partseg3.6-pyside/lib/python3.6/site-packages/vispy/color/color_array.py", line 55, in _user_to_rgba
    color = np.atleast_2d(color).astype(np.float32)
ValueError: setting an array element with a sequence.
```


Discussion:
Too many options in that if-else convolute:

https://github.com/vispy/vispy/blob/f1de04c52269d4288030b641d652afdac8e77183/vispy/color/color_array.py#L40-L55

In line 50 we would need to add `ColorArray` in the isinstance-check, for that to work, AFAICS. PullRequest welcome :smiley: 

Resolving patch:
diff --git a/vispy/color/color_array.py b/vispy/color/color_array.py
--- a/vispy/color/color_array.py
+++ b/vispy/color/color_array.py
@@ -47,7 +47,7 @@ def _user_to_rgba(color, expand=True, clip=False):
         color = color.rgba
     # We have to treat this specially
     elif isinstance(color, (list, tuple)):
-        if any(isinstance(c, string_types) for c in color):
+        if any(isinstance(c, (string_types, ColorArray)) for c in color):
             color = [_user_to_rgba(c, expand=expand, clip=clip) for c in color]
             if any(len(c) > 1 for c in color):
                 raise RuntimeError('could not parse colors, are they nested?')


Test patch:
diff --git a/vispy/color/tests/test_color.py b/vispy/color/tests/test_color.py
--- a/vispy/color/tests/test_color.py
+++ b/vispy/color/tests/test_color.py
@@ -61,6 +61,11 @@ def test_color_array():
     x = ColorArray(color_space="hsv")
     assert_array_equal(x.rgba[0], [0, 0, 0, 1])
 
+    x = ColorArray([Color((0, 0, 0)), Color((1, 1, 1))])
+    assert len(x.rgb) == 2
+    x = ColorArray([ColorArray((0, 0, 0)), ColorArray((1, 1, 1))])
+    assert len(x.rgb) == 2
+
 
 def test_color_interpretation():
     """Test basic color interpretation API"""
Repository: vispy/vispy

Issue:
Remove Python 2.7 wrapper around GzipFile
This module: https://github.com/vispy/vispy/blob/9dbe751dcdbaaa1d17d0a5885f15872b9b0741a8/vispy/ext/gzip_open.py

Is used in two places in vispy (one module) and only exists to be used by Python 2.7 environments which we no longer support. This module and it's usages should be replaced by `GzipFile`: https://docs.python.org/3/library/gzip.html#gzip.GzipFile

Usage 1:

https://github.com/vispy/vispy/blob/9dbe751dcdbaaa1d17d0a5885f15872b9b0741a8/vispy/io/wavefront.py#L70

Usage 2:

https://github.com/vispy/vispy/blob/9dbe751dcdbaaa1d17d0a5885f15872b9b0741a8/vispy/io/wavefront.py#L254

And of course the import in that module should be removed.


Discussion:
Hello, I would like to contribute, may I be assigned to this issue?

Done. Thanks.
Hey, I would like to work on this too. If it is possible may I be assigned?
Thank you.
@Kartik-byte Only if @jyoungiv has decided not to do it. @jyoungiv do you plan on working on this still?
Okay sure, I just saw that in my  last PR ,by mistake, I took a issue that someone else was assigned to . I felt bad. Not gonna do that this time. Still trying to learn github.
Sorry.

Resolving patch:
diff --git a/vispy/ext/gzip_open.py b/vispy/ext/gzip_open.py
deleted file mode 100644
--- a/vispy/ext/gzip_open.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) Vispy Development Team. All Rights Reserved.
-# Distributed under the (new) BSD License. See LICENSE.txt for more info.
-from gzip import GzipFile
-
-
-# Python < 2.7 doesn't have context handling
-class gzip_open(GzipFile):
-    def __enter__(self):
-        if hasattr(GzipFile, '__enter__'):
-            return GzipFile.__enter__(self)
-        else:
-            return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        if hasattr(GzipFile, '__exit__'):
-            return GzipFile.__exit__(self, exc_type, exc_value, traceback)
-        else:
-            return self.close()
diff --git a/vispy/io/wavefront.py b/vispy/io/wavefront.py
--- a/vispy/io/wavefront.py
+++ b/vispy/io/wavefront.py
@@ -20,9 +20,9 @@
 
 import numpy as np
 import time
+from gzip import GzipFile
 from os import path as op
 
-from ..ext.gzip_open import gzip_open
 from ..geometry import _calculate_normals
 from ..util import logger
 
@@ -67,7 +67,7 @@ def read(cls, fname):
         # Open file
         fmt = op.splitext(fname)[1].lower()
         assert fmt in ('.obj', '.gz')
-        opener = open if fmt == '.obj' else gzip_open
+        opener = open if fmt == '.obj' else GzipFile
         with opener(fname, 'rb') as f:
             try:
                 reader = WavefrontReader(f)
@@ -251,7 +251,7 @@ def write(cls, fname, vertices, faces, normals,
         if fmt not in ('.obj', '.gz'):
             raise ValueError('Filename must end with .obj or .gz, not "%s"'
                              % (fmt,))
-        opener = open if fmt == '.obj' else gzip_open
+        opener = open if fmt == '.obj' else GzipFile
         f = opener(fname, 'wb')
         try:
             writer = WavefrontWriter(f)


Test patch:
Repository: vispy/vispy

Issue:
LineVisual line smooth and width set after GL state is configured
In #2082 @almarklein updated the line visual to call `self.update_gl_state` to change `line_smooth` and `line_width`. The problem is, as I just realized while debugging #2107, that `update_gl_state` doesn't have an immediate effect on GL state. Only when `Visual._configure_gl_state` does the lower-level `gloo.set_state` call get executed. That happens on every `Visual.draw` call *BUT* before `_prepare_draw` is called. `_prepare_draw` is when these line smooth/width changes happen.

https://github.com/vispy/vispy/blob/184d854c7542f4c6df21bb4c7db3dd5b340dd900/vispy/visuals/visual.py#L442-L444


Discussion:
I'm kind of wondering if there are reasons GL state needs to be configured *before* `_prepare_draw`. I can only see `TextVisual` needing this, but it could run that manually if it really needs to. Right now this current behavior means that Visuals GL state is sent/configured on the GPU regardless of if its `_prepare_draw` thinks it should be drawn.
> I'm kind of wondering if there are reasons GL state needs to be configured _before_ `_prepare_draw`. I can only see `TextVisual` needing this, but it could run that manually if it really needs to. Right now this current behavior means that Visuals GL state is sent/configured on the GPU regardless of if its `_prepare_draw` thinks it should be drawn.

I was wondering the same. I don't see either why the state would need to be set when not drawing. Moving it down would also get rid of unnecessary glir commands.
I think the one issue may be if the `_prepare_draw` was doing something that depended on the GL state, but I can't think of anything right now. TextVisual is a special case as I mentioned because it has the option of drawing the glyphs on the CPU or GPU and has to change the GL state to due that properly (based on what I saw in the code).
@asnt or @almarklein if either of you want to make a PR that moves the configure call to below the `_prepare_draw` I'd be OK with that. I guess we could assume that if the tests pass then it is an OK change... :fearful: 

Resolving patch:
diff --git a/vispy/visuals/visual.py b/vispy/visuals/visual.py
--- a/vispy/visuals/visual.py
+++ b/vispy/visuals/visual.py
@@ -439,13 +439,14 @@ def _index_buffer(self, buf):
     def draw(self):
         if not self.visible:
             return
-        self._configure_gl_state()
         if self._prepare_draw(view=self) is False:
             return
 
         if self._vshare.draw_mode is None:
             raise ValueError("_draw_mode has not been set for visual %r" %
                              self)
+
+        self._configure_gl_state()
         try:
             self._program.draw(self._vshare.draw_mode,
                                self._vshare.index_buffer)


Test patch:
Repository: vispy/vispy

Issue:
`VolumeVisual` max/min intensity projection failure mode
The `VolumeVisual` has an interesting failure mode in both maximum and minimum intensity projection modes highlighted by @brisvag in the following [comment](https://github.com/napari/napari/pull/2934/files/c5c96e2b5faa854600f3d65d68a2ae01f47d8c7a#r660050470) which includes a nice video

If real maxima/minima are not found when the rays are cast, the value is searched for around `start_loc` of the ray anyway, causing the front faces of the cuboid to include some artefacts. 





Discussion:

Resolving patch:
diff --git a/vispy/visuals/volume.py b/vispy/visuals/volume.py
--- a/vispy/visuals/volume.py
+++ b/vispy/visuals/volume.py
@@ -278,7 +278,7 @@
 MIP_SNIPPETS = dict(
     before_loop="""
         float maxval = -99999.0; // The maximum encountered value
-        int maxi = 0;  // Where the maximum value was encountered
+        int maxi = -1;  // Where the maximum value was encountered
         """,
     in_loop="""
         if( val > maxval ) {
@@ -287,13 +287,15 @@
         }
         """,
     after_loop="""
-        // Refine search for max value
-        loc = start_loc + step * (float(maxi) - 0.5);
-        for (int i=0; i<10; i++) {
-            maxval = max(maxval, $sample(u_volumetex, loc).r);
-            loc += step * 0.1;
-        }
-        gl_FragColor = applyColormap(maxval);
+        // Refine search for max value, but only if anything was found
+        if ( maxi > -1 ) {{
+            loc = start_loc + step * (float(maxi) - 0.5);
+            for (int i=0; i<10; i++) {
+                maxval = max(maxval, $sample(u_volumetex, loc).r);
+                loc += step * 0.1;
+            }
+            gl_FragColor = applyColormap(maxval);
+        }}
         """,
 )
 MIP_FRAG_SHADER = FRAG_SHADER.format(**MIP_SNIPPETS)
@@ -326,7 +328,7 @@
 MINIP_SNIPPETS = dict(
     before_loop="""
         float minval = 99999.0; // The minimum encountered value
-        int mini = 0;  // Where the minimum value was encountered
+        int mini = -1;  // Where the minimum value was encountered
         """,
     in_loop="""
         if( val < minval ) {
@@ -335,13 +337,15 @@
         }
         """,
     after_loop="""
-        // Refine search for min value
-        loc = start_loc + step * (float(mini) - 0.5);
-        for (int i=0; i<10; i++) {
-            minval = min(minval, $sample(u_volumetex, loc).r);
-            loc += step * 0.1;
-        }
-        gl_FragColor = applyColormap(minval);
+        // Refine search for min value, but only if anything was found
+        if ( mini > -1 ) {{
+            loc = start_loc + step * (float(mini) - 0.5);
+            for (int i=0; i<10; i++) {
+                minval = min(minval, $sample(u_volumetex, loc).r);
+                loc += step * 0.1;
+            }
+            gl_FragColor = applyColormap(minval);
+        }}
         """,
 )
 MINIP_FRAG_SHADER = FRAG_SHADER.format(**MINIP_SNIPPETS)


Test patch:
Repository: vispy/vispy

Issue:
ImageVisual fails if data is all NaN
In my application I need to temporarily fill the texture (ImageVisual) with invalid data. I'd like if it showed up as transparent which I think I can handle with a custom shader, but the new NaN/Inf handling in the new scalable textures does not allow this for `clim='auto'` cases. I end up getting:

```
       ValueError: zero-size array to reduction operation minimum which has no identity
```

The reason is pretty obvious when looking at the code:

https://github.com/vispy/vispy/blob/d313742a00da77deaa8e1696d7fa74dc4aad19bc/vispy/visuals/_scalable_textures.py#L38-L40

If none of the data is finite then `data[np.isfinite(data)]` returns an empty array and the min/max fail after. I think a better behavior may be to check for a `0` size and return `0, inf`? Or maybe `-inf, inf`?


Discussion:
These `NaN` continue to hunt us. I guess that's what they do ...
Yeah...it's just new bugs with new features. What do you think on the returned clims for all-Nan/all-Inf data? I guess it could be `-inf, inf` or maybe even `nan, nan`...maybe.
It does not even matter much, does it, since all NaN's will be discarted or snap to the lower clim anyway. Maybe we should aim for the clim values to be finite, so  perhaps `(0, 0)`?
Yeah I suppose that fits with the general idea of what you would get if you did min/max on data that was all the same. Leaves it up to the specific texture subclass on how it wants to handle the equal clims. I'll try making a PR tonight or tomorrow.

Resolving patch:
diff --git a/vispy/visuals/_scalable_textures.py b/vispy/visuals/_scalable_textures.py
--- a/vispy/visuals/_scalable_textures.py
+++ b/vispy/visuals/_scalable_textures.py
@@ -36,8 +36,11 @@ def get_default_clim_from_data(data):
     max_finite = np.isfinite(max_value)
     if not (min_finite and max_finite):
         finite_data = data[np.isfinite(data)]
-        min_value = finite_data.min()
-        max_value = finite_data.max()
+        if finite_data.size:
+            min_value = finite_data.min()
+            max_value = finite_data.max()
+        else:
+            min_value = max_value = 0  # no finite values in the data
 
     return min_value, max_value
 


Test patch:
diff --git a/vispy/visuals/tests/test_scalable_textures.py b/vispy/visuals/tests/test_scalable_textures.py
--- a/vispy/visuals/tests/test_scalable_textures.py
+++ b/vispy/visuals/tests/test_scalable_textures.py
@@ -77,6 +77,18 @@ def test_default_clim_non_finite():
     clim = get_default_clim_from_data(data)
     assert clim == (5, 25)
 
+    data = np.array([np.nan, np.nan, np.nan]).astype(np.float32)
+    clim = get_default_clim_from_dtype(data.dtype)
+    assert clim == (0, 1)
+    clim = get_default_clim_from_data(data)
+    assert clim == (0, 0)
+
+    data = np.array([np.nan, np.inf, -np.inf]).astype(np.float32)
+    clim = get_default_clim_from_dtype(data.dtype)
+    assert clim == (0, 1)
+    clim = get_default_clim_from_data(data)
+    assert clim == (0, 0)
+
 
 def test_clim_handling_cpu():
Repository: vispy/vispy

Issue:
Remove warning on import about missing networkx dependency
Currently, importing vispy (or some submodule) without networkx installed results in a warning about installing networkx, even if the user/downstream library has no intention of using the graph layout. The warning should be delayed and turned into an error, as noted by @djhoese [here](https://github.com/napari/napari/issues/2979#issuecomment-874159877).

xref  napari/napari#2979


Discussion:

Resolving patch:
diff --git a/vispy/visuals/graphs/layouts/networkx_layout.py b/vispy/visuals/graphs/layouts/networkx_layout.py
--- a/vispy/visuals/graphs/layouts/networkx_layout.py
+++ b/vispy/visuals/graphs/layouts/networkx_layout.py
@@ -6,9 +6,6 @@
 try:
     import networkx as nx
 except ModuleNotFoundError:
-    import warnings
-    warnings.warn(
-        "Networkx not found, please install network to use its layouts")
     nx = None
 
 
@@ -27,6 +24,8 @@ def __init__(self, graph=None, layout=None, **kwargs):
         kwargs: dict, optional
         when layout is :str: :kwargs: will act as a setting dictionary for the layout function of networkx
         """
+        if nx is None:
+            raise ValueError("networkx not found, please install networkx to use its layouts")
         if isinstance(graph, type(None)):
             raise ValueError("Requires networkx input")
         self.graph = graph
@@ -37,17 +36,14 @@ def __init__(self, graph=None, layout=None, **kwargs):
 
         # check for networkx
         elif isinstance(layout, str):
-            if nx:
-                if not layout.endswith("_layout"):
-                    layout += "_layout"  # append for nx
-                layout_function = getattr(nx, layout)
-                if layout_function:
-                    self.positions = np.asarray(
-                        [i for i in dict(layout_function(graph, **kwargs)).values()])
-                else:
-                    raise ValueError("Check networkx for layouts")
+            if not layout.endswith("_layout"):
+                layout += "_layout"  # append for nx
+            layout_function = getattr(nx, layout)
+            if layout_function:
+                self.positions = np.asarray(
+                    [i for i in dict(layout_function(graph, **kwargs)).values()])
             else:
-                raise ValueError("networkx not found")
+                raise ValueError("Check networkx for layouts")
         # assume dict from networkx; values are 2-array
         elif isinstance(layout, dict):
             self.positions = np.asarray([i for i in layout.values()])


Test patch:
Repository: vispy/vispy

Issue:
Update volume_plane.py to allow timer to be stopped
I noticed the volume_plane.py has keyboard handling for moving the plane through the volume, but it doesn't allow you to stop the animation so these events don't actually mean much.

@alisterburt do you have time to fix this? Tradition is to use the spacebar (`" "`) to toggle the timer start/stop


Discussion:
oh yeah! Thanks for pointing that out -  I forgot to integrate the animated example with the keybindings, will try get to that sorted this evening
Thanks @alisterburt. So is this something you think you'll have time for today? If not, I can try to find the time and then make a 0.8.0 release with this fix. However, I have a kid staying home sick today so not sure I'll have a bunch of dedicated time. Let me know your availability. Thanks.
Will have a go after lunch - sorry to hear about the illness, hope they get better soon! 🙂 

Resolving patch:
diff --git a/examples/scene/volume_plane.py b/examples/scene/volume_plane.py
--- a/examples/scene/volume_plane.py
+++ b/examples/scene/volume_plane.py
@@ -13,6 +13,7 @@
 * 2 -  toggle between volume rendering modes ('volume', 'plane')
 * [] - shift plane along plane normal
 * {} - decrease/increase plane thickness
+* Spacebar - stop/start animation
 
 * x/y/z/o - set plane normal along x/y/z or [1,1,1] oblique axis
 """
@@ -113,7 +114,6 @@ def on_key_press(event):
         elif event.text == ']':
             plane.plane_position += 2 * shift
         print(f"plane position: {plane.plane_position}")
-
     elif event.text == 'x':
         plane.plane_normal = [0, 0, 1]
     elif event.text == 'y':
@@ -122,6 +122,11 @@ def on_key_press(event):
         plane.plane_normal = [1, 0, 0]
     elif event.text == 'o':
         plane.plane_normal = [1, 1, 1]
+    elif event.text == ' ':
+        if timer.running:
+            timer.stop()
+        else:
+            timer.start()
 
 
 def move_plane(event):


Test patch: