Repository: qutebrowser/qutebrowser

Issue:
config.bind allows an empty string
`config.bind('x', '')` is possible and does this:

```
  File "/home/florian/proj/qutebrowser/git/qutebrowser/config/config.py", line 113, in wrapper
    return func(wrapper_self)
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/basekeyparser.py", line 278, in _on_config_changed
    self._read_config()
  File "/home/florian/proj/qutebrowser/git/qutebrowser/keyinput/basekeyparser.py", line 300, in _read_config
    assert cmd
AssertionError
```


Discussion:
Should this simply be ignored and handled liked `None` or should an exception be raised? For the latter this should probably work:

```patch
Index: qutebrowser/config/config.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- qutebrowser/config/config.py	(revision be08cee63c001e766950e2b52ca859918be28d3b)
+++ qutebrowser/config/config.py	(revision )
@@ -170,6 +170,11 @@
 
     def bind(self, key, command, *, mode, save_yaml=False):
         """Add a new binding from key to command."""
+        if command is not None and not command.strip():
+            raise configexc.KeybindingError(
+                "Can not add binding '{}' with empty command in {} "
+                'mode'.format(key, mode))
+
         key = self._prepare(key, mode)
         log.keyboard.vdebug("Adding binding {} -> {} in mode {}.".format(
             key, command, mode))```
I think raising an error would be the right thing to do here (but it'll also need a test :wink:). Is this something you'd like to work on?
I'll try if you give me a pointer for the testing: what kind of test would be most appropriate for this? I assume an addition to the py.tests dealing with the ConfigApi?
You can simply add a unit test for the `Config` object, many of those already exist - see the `TestKeyConfig` class in `tests/unit/config/test_config.py`.

Resolving patch:
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -170,6 +170,11 @@ def get_command(self, key, mode):
 
     def bind(self, key, command, *, mode, save_yaml=False):
         """Add a new binding from key to command."""
+        if command is not None and not command.strip():
+            raise configexc.KeybindingError(
+                "Can't add binding '{}' with empty command in {} "
+                'mode'.format(key, mode))
+
         key = self._prepare(key, mode)
         log.keyboard.vdebug("Adding binding {} -> {} in mode {}.".format(
             key, command, mode))


Test patch:
diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py
--- a/tests/unit/config/test_config.py
+++ b/tests/unit/config/test_config.py
@@ -285,6 +285,12 @@ def test_unbound_twice(self, key_config_stub, config_stub, no_bindings):
         key_config_stub.unbind('a')
         assert key_config_stub.get_command('a', mode='normal') is None
 
+    def test_empty_command(self, key_config_stub):
+        """Try binding a key to an empty command."""
+        message = "Can't add binding 'x' with empty command in normal mode"
+        with pytest.raises(configexc.KeybindingError, match=message):
+            key_config_stub.bind('x', ' ', mode='normal')
+
 
 class TestConfig: