Repository: qtile/qtile

Issue:
DmenuRun doesn't take colors from extension_defaults
This is split from https://github.com/qtile/qtile/issues/2730

Qtile version: 0.18.2.dev130+g3fe5e0d1
The problem starts in commit: https://github.com/qtile/qtile/commit/d7016e1dc27680437d89efb322d5e7db03e6b8fe

## test case
```
    EzKey("M-r", lazy.run_extension(extension.DmenuRun()), desc='dmenu run'),
    ...
extension_defaults = dict(
    background='#000000',
    foreground='#00ff00',
    selected_background='#00ff00',
    selected_foreground='#000000'
)
```

No errors
DmenuRun doesn't take colors from extension_defaults
This is split from https://github.com/qtile/qtile/issues/2730

Qtile version: 0.18.2.dev130+g3fe5e0d1
The problem starts in commit: https://github.com/qtile/qtile/commit/d7016e1dc27680437d89efb322d5e7db03e6b8fe

## test case
```
    EzKey("M-r", lazy.run_extension(extension.DmenuRun()), desc='dmenu run'),
    ...
extension_defaults = dict(
    background='#000000',
    foreground='#00ff00',
    selected_background='#00ff00',
    selected_foreground='#000000'
)
```

No errors


Discussion:
Ok. This one is an easy fix. Will submit a PR for it shortly.
Ok. This one is an easy fix. Will submit a PR for it shortly.

Resolving patch:
diff --git a/libqtile/extension/base.py b/libqtile/extension/base.py
--- a/libqtile/extension/base.py
+++ b/libqtile/extension/base.py
@@ -47,13 +47,15 @@ def __init__(self, **config):
         configurable.Configurable.__init__(self, **config)
         self.add_defaults(_Extension.defaults)
         _Extension.installed_extensions.append(self)
-        self._check_colors()
 
     def _check_colors(self):
         """
         dmenu needs colours to be in #rgb or #rrggbb format.
 
         Checks colour value, removes invalid values and adds # if missing.
+
+        NB This should not be called in _Extension.__init__ as _Extension.global_defaults
+        may not have been set at this point.
         """
         for c in ["background", "foreground", "selected_background", "selected_foreground"]:
             col = getattr(self, c, None)
@@ -74,6 +76,7 @@ def _check_colors(self):
 
     def _configure(self, qtile):
         self.qtile = qtile
+        self._check_colors()
 
     def run(self):
         """
diff --git a/libqtile/extension/command_set.py b/libqtile/extension/command_set.py
--- a/libqtile/extension/command_set.py
+++ b/libqtile/extension/command_set.py
@@ -18,8 +18,6 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-from os import system
-
 from libqtile.extension.dmenu import Dmenu
 
 
@@ -61,7 +59,7 @@ def run(self):
 
         if self.pre_commands:
             for cmd in self.pre_commands:
-                system(cmd)
+                self.qtile.cmd_spawn(cmd)
 
         out = super(CommandSet, self).run(items=self.commands.keys())
 
@@ -76,4 +74,4 @@ def run(self):
         if sout not in self.commands:
             return
 
-        system(self.commands[sout])
+        self.qtile.cmd_spawn(self.commands[sout])


Test patch:
diff --git a/test/extension/test_base.py b/test/extension/test_base.py
--- a/test/extension/test_base.py
+++ b/test/extension/test_base.py
@@ -35,9 +35,29 @@
 @pytest.mark.parametrize("value,expected", parameters)
 def test_valid_colours(value, expected):
     extension = _Extension(foreground=value)
+    extension._configure(None)
     assert extension.foreground == expected
 
 
+def test_valid_colours_extension_defaults(monkeypatch):
+    defaults = {
+        "foreground": "00ff00",
+        "background": "000000",
+        "selected_foreground": "000000",
+        "selected_background": "00ff00",
+    }
+    extension = _Extension(foreground="0000ff")
+
+    # Set defaults after widget is created to mimic behaviour of extension being
+    # initialised in config.
+    monkeypatch.setattr(_Extension, "global_defaults", defaults)
+    extension._configure(None)
+    assert extension.foreground == "#0000ff"
+    assert extension.background == "#000000"
+    assert extension.selected_foreground == "#000000"
+    assert extension.selected_background == "#00ff00"
+
+
 def test_base_methods():
     class FakeQtile:
         pass
diff --git a/test/extension/test_command_set.py b/test/extension/test_command_set.py
--- a/test/extension/test_command_set.py
+++ b/test/extension/test_command_set.py
@@ -25,13 +25,19 @@
 from libqtile.log_utils import init_log, logger
 
 
+@pytest.fixture
+def fake_qtile():
+    class FakeQtile:
+        def cmd_spawn(self, value):
+            logger.warning(value)
+
+    yield FakeQtile()
+
+
 @pytest.fixture
 def log_extension_output(monkeypatch):
     init_log(logging.WARNING, log_path=None, log_color=False)
 
-    def fake_system(value):
-        logger.warning(value)
-
     def fake_popen(cmd, *args, **kwargs):
         class PopenObj:
             def communicate(self, value_in, *args):
@@ -42,14 +48,13 @@ def communicate(self, value_in, *args):
 
         return PopenObj()
 
-    monkeypatch.setattr("libqtile.extension.command_set.system", fake_system)
     monkeypatch.setattr("libqtile.extension.base.Popen", fake_popen)
 
     yield
 
 
 @pytest.mark.usefixtures("log_extension_output")
-def test_command_set_valid_command(caplog):
+def test_command_set_valid_command(caplog, fake_qtile):
     """Extension should run pre-commands and selected command."""
     extension = CommandSet(
         pre_commands=["run pre-command"],
@@ -57,7 +62,7 @@ def test_command_set_valid_command(caplog):
             "key": "run testcommand"
         }
     )
-    extension._configure(None)
+    extension._configure(fake_qtile)
     extension.run()
 
     assert caplog.record_tuples == [
@@ -67,7 +72,7 @@ def test_command_set_valid_command(caplog):
 
 
 @pytest.mark.usefixtures("log_extension_output")
-def test_command_set_invalid_command(caplog):
+def test_command_set_invalid_command(caplog, fake_qtile):
     """Where the key is not in "commands", no command will be run."""
     extension = CommandSet(
         pre_commands=["run pre-command"],
@@ -75,7 +80,7 @@ def test_command_set_invalid_command(caplog):
             "missing": "run testcommand"
         }
     )
-    extension._configure(None)
+    extension._configure(fake_qtile)
     extension.run()
 
     assert caplog.record_tuples == [