# Buggy code
def _set_keys(self, keys):
    if keys is not None:
        if isinstance(keys, string_types):
            if keys != "interactive":
                raise ValueError(
                    'keys, if string, must be "interactive", not %s' % (keys,)
                )

            def toggle_fs():
                self.fullscreen = not self.fullscreen

            keys = dict(escape="close", F11=toggle_fs)
    else:
        keys = {}
    if not isinstance(keys, dict):
        raise TypeError("keys must be a dict, str, or None")
    if len(keys) > 0:
        # ensure all are callable
        for key, val in keys.items():
            if isinstance(val, string_types):
                new_val = getattr(self, val, None)
                if new_val is None:
                    raise ValueError("value %s is not an attribute of Canvas" % val)
                val = new_val
            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

        def keys_check(event):
            use_name = event.key.name.lower()
            if use_name in self._keys_check:
                self._keys_check[use_name]()

        self.events.key_press.connect(keys_check, ref=True)


# Traceback (AttributeError)
WARNING: Traceback (most recent call last):
File ".\examples\demo\gloo\brain.py", line 156, in <module>
app.run()
File "D:\Git\vispy\vispy\app\_default_app.py", line 54, in run
return default_app.run()
File "D:\Git\vispy\vispy\app\application.py", line 88, in run
return self._backend._vispy_run()
File "D:\Git\vispy\vispy\app\backends\_qt.py", line 175, in _vispy_run
return app.exec_()
File "D:\Git\vispy\vispy\app\backends\_qt.py", line 391, in keyPressEvent
self._keyEvent(self._vispy_canvas.events.key_press, ev)
File "D:\Git\vispy\vispy\app\backends\_qt.py", line 406, in _keyEvent
func(native=ev, key=key, text=text_type(ev.text()), modifiers=mod)
File "D:\Git\vispy\vispy\util\event.py", line 418, in __call__
self._invoke_callback(cb, event)
File "D:\Git\vispy\vispy\util\event.py", line 446, in _invoke_callback
self, cb_event=(cb, event))
<< caught exception here: >>
File "D:\Git\vispy\vispy\util\event.py", line 435, in _invoke_callback
cb(event)
File "D:\Git\vispy\vispy\app\canvas.py", line 220, in keys_check
use_name = event.key.name.lower()
AttributeError: 'NoneType' object has no attribute 'name'
WARNING: Error invoking callback <function keys_check at 0x00000000067C73C8> for
event: <KeyEvent blocked=False handled=False key=None modifiers=() native=<PyQt
4.QtGui.QKeyEvent object at 0x0000000007230BF8> source=<Vispy canvas (PyQt4 (qt)
backend) at 0x72a10f0L> sources=[<Vispy canvas (PyQt4 (qt) backend) at 0x72a10f
0L>] text=² type=key_press>

# Fixed code
def _set_keys(self, keys):
    if keys is not None:
        if isinstance(keys, string_types):
            if keys != "interactive":
                raise ValueError(
                    'keys, if string, must be "interactive", not %s' % (keys,)
                )

            def toggle_fs():
                self.fullscreen = not self.fullscreen

            keys = dict(escape="close", F11=toggle_fs)
    else:
        keys = {}
    if not isinstance(keys, dict):
        raise TypeError("keys must be a dict, str, or None")
    if len(keys) > 0:
        # ensure all are callable
        for key, val in keys.items():
            if isinstance(val, string_types):
                new_val = getattr(self, val, None)
                if new_val is None:
                    raise ValueError("value %s is not an attribute of Canvas" % val)
                val = new_val
            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

        def keys_check(event):
            if event.key is not None:
                use_name = event.key.name.lower()
                if use_name in self._keys_check:
                    self._keys_check[use_name]()

        self.events.key_press.connect(keys_check, ref=True)