We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libqtile/extension/command_set.py`
Content:
```
1 # Copyright (C) 2018, zordsdavini
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a copy
4 # of this software and associated documentation files (the "Software"), to deal
5 # in the Software without restriction, including without limitation the rights
6 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 # copies of the Software, and to permit persons to whom the Software is
8 # furnished to do so, subject to the following conditions:
9 #
10 # The above copyright notice and this permission notice shall be included in
11 # all copies or substantial portions of the Software.
12 #
13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 # SOFTWARE.
20 
21 from os import system
22 
23 from libqtile.extension.dmenu import Dmenu
24 
25 
26 class CommandSet(Dmenu):
27     """
28     Give list of commands to be executed in dmenu style.
29 
30     ex. manage mocp deamon:
31 
32     .. code-block:: python
33 
34         Key([mod], 'm', lazy.run_extension(extension.CommandSet(
35             commands={
36                 'play/pause': '[ $(mocp -i | wc -l) -lt 2 ] && mocp -p || mocp -G',
37                 'next': 'mocp -f',
38                 'previous': 'mocp -r',
39                 'quit': 'mocp -x',
40                 'open': 'urxvt -e mocp',
41                 'shuffle': 'mocp -t shuffle',
42                 'repeat': 'mocp -t repeat',
43                 },
44             pre_commands=['[ $(mocp -i | wc -l) -lt 1 ] && mocp -S'],
45             **Theme.dmenu))),
46 
47     """
48 
49     defaults = [
50         ("commands", None, "dictionary of commands where key is runable command"),
51         ("pre_commands", None, "list of commands to be executed before getting dmenu answer"),
52     ]
53 
54     def __init__(self, **config):
55         Dmenu.__init__(self, **config)
56         self.add_defaults(CommandSet.defaults)
57 
58     def run(self):
59         if not self.commands:
60             return
61 
62         if self.pre_commands:
63             for cmd in self.pre_commands:
64                 system(cmd)
65 
66         out = super(CommandSet, self).run(items=self.commands.keys())
67 
68         try:
69             sout = out.rstrip('\n')
70         except AttributeError:
71             # out is not a string (for example it's a Popen object returned
72             # by super(WindowList, self).run() when there are no menu items to
73             # list
74             return
75 
76         if sout not in self.commands:
77             return
78 
79         system(self.commands[sout])
80
```

Path: `libqtile/extension/base.py`
Content:
```
1 # Copyright (c) 2017 Dario Giovannetti
2 # Copyright (c) 2021 elParaguayo
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
10 #
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 # SOFTWARE.
21 import re
22 import shlex
23 from subprocess import PIPE, Popen
24 from typing import Any, List, Tuple  # noqa: F401
25 
26 from libqtile import configurable
27 from libqtile.log_utils import logger
28 
29 RGB = re.compile(r"^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$")
30 
31 
32 class _Extension(configurable.Configurable):
33     """Base Extension class"""
34 
35     installed_extensions = []  # type: List
36 
37     defaults = [
38         ("font", "sans", "defines the font name to be used"),
39         ("fontsize", None, "defines the font size to be used"),
40         ("background", None, "defines the normal background color (#RGB or #RRGGBB)"),
41         ("foreground", None, "defines the normal foreground color (#RGB or #RRGGBB)"),
42         ("selected_background", None, "defines the selected background color (#RGB or #RRGGBB)"),
43         ("selected_foreground", None, "defines the selected foreground color (#RGB or #RRGGBB)"),
44     ]
45 
46     def __init__(self, **config):
47         configurable.Configurable.__init__(self, **config)
48         self.add_defaults(_Extension.defaults)
49         _Extension.installed_extensions.append(self)
50         self._check_colors()
51 
52     def _check_colors(self):
53         """
54         dmenu needs colours to be in #rgb or #rrggbb format.
55 
56         Checks colour value, removes invalid values and adds # if missing.
57         """
58         for c in ["background", "foreground", "selected_background", "selected_foreground"]:
59             col = getattr(self, c, None)
60             if col is None:
61                 continue
62 
63             if not isinstance(col, str) or not RGB.match(col):
64                 logger.warning(
65                     f"Invalid extension '{c}' color: {col}. "
66                     f"Must be #RGB or #RRGGBB string."
67                 )
68                 setattr(self, c, None)
69                 continue
70 
71             if not col.startswith("#"):
72                 col = f"#{col}"
73                 setattr(self, c, col)
74 
75     def _configure(self, qtile):
76         self.qtile = qtile
77 
78     def run(self):
79         """
80         This method must be implemented by the subclasses.
81         """
82         raise NotImplementedError()
83 
84 
85 class RunCommand(_Extension):
86     """
87     Run an arbitrary command.
88 
89     Mostly useful as a superclass for more specific extensions that need to
90     interact with the qtile object.
91 
92     Also consider simply using lazy.spawn() or writing a
93     `client <http://docs.qtile.org/en/latest/manual/commands/scripting.html>`_.
94     """
95     defaults = [
96         # NOTE: Do not use a list as a default value, since it would be shared
97         #       among all the objects inheriting this class, and if one of them
98         #       modified it, all the other objects would see the modified list;
99         #       use a string or a tuple instead, which are immutable
100         ("command", None, "the command to be launched (string or list with arguments)"),
101     ]  # type: List[Tuple[str, Any, str]]
102 
103     def __init__(self, **config):
104         _Extension.__init__(self, **config)
105         self.add_defaults(RunCommand.defaults)
106         self.configured_command = None
107 
108     def run(self):
109         """
110         An extension can inherit this class, define configured_command and use
111         the process object by overriding this method and using super():
112 
113         .. code-block:: python
114 
115             def _configure(self, qtile):
116                 Superclass._configure(self, qtile)
117                 self.configured_command = "foo --bar"
118 
119             def run(self):
120                 process = super(Subclass, self).run()
121         """
122         if self.configured_command:
123             if isinstance(self.configured_command, str):
124                 self.configured_command = shlex.split(self.configured_command)
125             # Else assume that self.configured_command is already a sequence
126         else:
127             self.configured_command = self.command
128         return Popen(self.configured_command, stdout=PIPE, stdin=PIPE)
129
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
         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 _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 @@
 
         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 @@
         if sout not in self.commands:
             return
 
-        system(self.commands[sout])
+        self.qtile.cmd_spawn(self.commands[sout])

{"golden_diff": "diff --git a/libqtile/extension/base.py b/libqtile/extension/base.py\n--- a/libqtile/extension/base.py\n+++ b/libqtile/extension/base.py\n@@ -47,13 +47,15 @@\n         configurable.Configurable.__init__(self, **config)\n         self.add_defaults(_Extension.defaults)\n         _Extension.installed_extensions.append(self)\n-        self._check_colors()\n \n     def _check_colors(self):\n         \"\"\"\n         dmenu needs colours to be in #rgb or #rrggbb format.\n \n         Checks colour value, removes invalid values and adds # if missing.\n+\n+        NB This should not be called in _Extension.__init__ as _Extension.global_defaults\n+        may not have been set at this point.\n         \"\"\"\n         for c in [\"background\", \"foreground\", \"selected_background\", \"selected_foreground\"]:\n             col = getattr(self, c, None)\n@@ -74,6 +76,7 @@\n \n     def _configure(self, qtile):\n         self.qtile = qtile\n+        self._check_colors()\n \n     def run(self):\n         \"\"\"\ndiff --git a/libqtile/extension/command_set.py b/libqtile/extension/command_set.py\n--- a/libqtile/extension/command_set.py\n+++ b/libqtile/extension/command_set.py\n@@ -18,8 +18,6 @@\n # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n # SOFTWARE.\n \n-from os import system\n-\n from libqtile.extension.dmenu import Dmenu\n \n \n@@ -61,7 +59,7 @@\n \n         if self.pre_commands:\n             for cmd in self.pre_commands:\n-                system(cmd)\n+                self.qtile.cmd_spawn(cmd)\n \n         out = super(CommandSet, self).run(items=self.commands.keys())\n \n@@ -76,4 +74,4 @@\n         if sout not in self.commands:\n             return\n \n-        system(self.commands[sout])\n+        self.qtile.cmd_spawn(self.commands[sout])\n", "issue": "DmenuRun doesn't take colors from extension_defaults\nThis is split from https://github.com/qtile/qtile/issues/2730\r\n\r\nQtile version: 0.18.2.dev130+g3fe5e0d1\r\nThe problem starts in commit: https://github.com/qtile/qtile/commit/d7016e1dc27680437d89efb322d5e7db03e6b8fe\r\n\r\n## test case\r\n```\r\n    EzKey(\"M-r\", lazy.run_extension(extension.DmenuRun()), desc='dmenu run'),\r\n    ...\r\nextension_defaults = dict(\r\n    background='#000000',\r\n    foreground='#00ff00',\r\n    selected_background='#00ff00',\r\n    selected_foreground='#000000'\r\n)\r\n```\r\n\r\nNo errors\nDmenuRun doesn't take colors from extension_defaults\nThis is split from https://github.com/qtile/qtile/issues/2730\r\n\r\nQtile version: 0.18.2.dev130+g3fe5e0d1\r\nThe problem starts in commit: https://github.com/qtile/qtile/commit/d7016e1dc27680437d89efb322d5e7db03e6b8fe\r\n\r\n## test case\r\n```\r\n    EzKey(\"M-r\", lazy.run_extension(extension.DmenuRun()), desc='dmenu run'),\r\n    ...\r\nextension_defaults = dict(\r\n    background='#000000',\r\n    foreground='#00ff00',\r\n    selected_background='#00ff00',\r\n    selected_foreground='#000000'\r\n)\r\n```\r\n\r\nNo errors\n", "before_files": [{"content": "# Copyright (C) 2018, zordsdavini\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n\nfrom os import system\n\nfrom libqtile.extension.dmenu import Dmenu\n\n\nclass CommandSet(Dmenu):\n    \"\"\"\n    Give list of commands to be executed in dmenu style.\n\n    ex. manage mocp deamon:\n\n    .. code-block:: python\n\n        Key([mod], 'm', lazy.run_extension(extension.CommandSet(\n            commands={\n                'play/pause': '[ $(mocp -i | wc -l) -lt 2 ] && mocp -p || mocp -G',\n                'next': 'mocp -f',\n                'previous': 'mocp -r',\n                'quit': 'mocp -x',\n                'open': 'urxvt -e mocp',\n                'shuffle': 'mocp -t shuffle',\n                'repeat': 'mocp -t repeat',\n                },\n            pre_commands=['[ $(mocp -i | wc -l) -lt 1 ] && mocp -S'],\n            **Theme.dmenu))),\n\n    \"\"\"\n\n    defaults = [\n        (\"commands\", None, \"dictionary of commands where key is runable command\"),\n        (\"pre_commands\", None, \"list of commands to be executed before getting dmenu answer\"),\n    ]\n\n    def __init__(self, **config):\n        Dmenu.__init__(self, **config)\n        self.add_defaults(CommandSet.defaults)\n\n    def run(self):\n        if not self.commands:\n            return\n\n        if self.pre_commands:\n            for cmd in self.pre_commands:\n                system(cmd)\n\n        out = super(CommandSet, self).run(items=self.commands.keys())\n\n        try:\n            sout = out.rstrip('\\n')\n        except AttributeError:\n            # out is not a string (for example it's a Popen object returned\n            # by super(WindowList, self).run() when there are no menu items to\n            # list\n            return\n\n        if sout not in self.commands:\n            return\n\n        system(self.commands[sout])\n", "path": "libqtile/extension/command_set.py"}, {"content": "# Copyright (c) 2017 Dario Giovannetti\n# Copyright (c) 2021 elParaguayo\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nimport re\nimport shlex\nfrom subprocess import PIPE, Popen\nfrom typing import Any, List, Tuple  # noqa: F401\n\nfrom libqtile import configurable\nfrom libqtile.log_utils import logger\n\nRGB = re.compile(r\"^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$\")\n\n\nclass _Extension(configurable.Configurable):\n    \"\"\"Base Extension class\"\"\"\n\n    installed_extensions = []  # type: List\n\n    defaults = [\n        (\"font\", \"sans\", \"defines the font name to be used\"),\n        (\"fontsize\", None, \"defines the font size to be used\"),\n        (\"background\", None, \"defines the normal background color (#RGB or #RRGGBB)\"),\n        (\"foreground\", None, \"defines the normal foreground color (#RGB or #RRGGBB)\"),\n        (\"selected_background\", None, \"defines the selected background color (#RGB or #RRGGBB)\"),\n        (\"selected_foreground\", None, \"defines the selected foreground color (#RGB or #RRGGBB)\"),\n    ]\n\n    def __init__(self, **config):\n        configurable.Configurable.__init__(self, **config)\n        self.add_defaults(_Extension.defaults)\n        _Extension.installed_extensions.append(self)\n        self._check_colors()\n\n    def _check_colors(self):\n        \"\"\"\n        dmenu needs colours to be in #rgb or #rrggbb format.\n\n        Checks colour value, removes invalid values and adds # if missing.\n        \"\"\"\n        for c in [\"background\", \"foreground\", \"selected_background\", \"selected_foreground\"]:\n            col = getattr(self, c, None)\n            if col is None:\n                continue\n\n            if not isinstance(col, str) or not RGB.match(col):\n                logger.warning(\n                    f\"Invalid extension '{c}' color: {col}. \"\n                    f\"Must be #RGB or #RRGGBB string.\"\n                )\n                setattr(self, c, None)\n                continue\n\n            if not col.startswith(\"#\"):\n                col = f\"#{col}\"\n                setattr(self, c, col)\n\n    def _configure(self, qtile):\n        self.qtile = qtile\n\n    def run(self):\n        \"\"\"\n        This method must be implemented by the subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n\nclass RunCommand(_Extension):\n    \"\"\"\n    Run an arbitrary command.\n\n    Mostly useful as a superclass for more specific extensions that need to\n    interact with the qtile object.\n\n    Also consider simply using lazy.spawn() or writing a\n    `client <http://docs.qtile.org/en/latest/manual/commands/scripting.html>`_.\n    \"\"\"\n    defaults = [\n        # NOTE: Do not use a list as a default value, since it would be shared\n        #       among all the objects inheriting this class, and if one of them\n        #       modified it, all the other objects would see the modified list;\n        #       use a string or a tuple instead, which are immutable\n        (\"command\", None, \"the command to be launched (string or list with arguments)\"),\n    ]  # type: List[Tuple[str, Any, str]]\n\n    def __init__(self, **config):\n        _Extension.__init__(self, **config)\n        self.add_defaults(RunCommand.defaults)\n        self.configured_command = None\n\n    def run(self):\n        \"\"\"\n        An extension can inherit this class, define configured_command and use\n        the process object by overriding this method and using super():\n\n        .. code-block:: python\n\n            def _configure(self, qtile):\n                Superclass._configure(self, qtile)\n                self.configured_command = \"foo --bar\"\n\n            def run(self):\n                process = super(Subclass, self).run()\n        \"\"\"\n        if self.configured_command:\n            if isinstance(self.configured_command, str):\n                self.configured_command = shlex.split(self.configured_command)\n            # Else assume that self.configured_command is already a sequence\n        else:\n            self.configured_command = self.command\n        return Popen(self.configured_command, stdout=PIPE, stdin=PIPE)\n", "path": "libqtile/extension/base.py"}], "after_files": [{"content": "# Copyright (C) 2018, zordsdavini\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n\nfrom libqtile.extension.dmenu import Dmenu\n\n\nclass CommandSet(Dmenu):\n    \"\"\"\n    Give list of commands to be executed in dmenu style.\n\n    ex. manage mocp deamon:\n\n    .. code-block:: python\n\n        Key([mod], 'm', lazy.run_extension(extension.CommandSet(\n            commands={\n                'play/pause': '[ $(mocp -i | wc -l) -lt 2 ] && mocp -p || mocp -G',\n                'next': 'mocp -f',\n                'previous': 'mocp -r',\n                'quit': 'mocp -x',\n                'open': 'urxvt -e mocp',\n                'shuffle': 'mocp -t shuffle',\n                'repeat': 'mocp -t repeat',\n                },\n            pre_commands=['[ $(mocp -i | wc -l) -lt 1 ] && mocp -S'],\n            **Theme.dmenu))),\n\n    \"\"\"\n\n    defaults = [\n        (\"commands\", None, \"dictionary of commands where key is runable command\"),\n        (\"pre_commands\", None, \"list of commands to be executed before getting dmenu answer\"),\n    ]\n\n    def __init__(self, **config):\n        Dmenu.__init__(self, **config)\n        self.add_defaults(CommandSet.defaults)\n\n    def run(self):\n        if not self.commands:\n            return\n\n        if self.pre_commands:\n            for cmd in self.pre_commands:\n                self.qtile.cmd_spawn(cmd)\n\n        out = super(CommandSet, self).run(items=self.commands.keys())\n\n        try:\n            sout = out.rstrip('\\n')\n        except AttributeError:\n            # out is not a string (for example it's a Popen object returned\n            # by super(WindowList, self).run() when there are no menu items to\n            # list\n            return\n\n        if sout not in self.commands:\n            return\n\n        self.qtile.cmd_spawn(self.commands[sout])\n", "path": "libqtile/extension/command_set.py"}, {"content": "# Copyright (c) 2017 Dario Giovannetti\n# Copyright (c) 2021 elParaguayo\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nimport re\nimport shlex\nfrom subprocess import PIPE, Popen\nfrom typing import Any, List, Tuple  # noqa: F401\n\nfrom libqtile import configurable\nfrom libqtile.log_utils import logger\n\nRGB = re.compile(r\"^#?([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$\")\n\n\nclass _Extension(configurable.Configurable):\n    \"\"\"Base Extension class\"\"\"\n\n    installed_extensions = []  # type: List\n\n    defaults = [\n        (\"font\", \"sans\", \"defines the font name to be used\"),\n        (\"fontsize\", None, \"defines the font size to be used\"),\n        (\"background\", None, \"defines the normal background color (#RGB or #RRGGBB)\"),\n        (\"foreground\", None, \"defines the normal foreground color (#RGB or #RRGGBB)\"),\n        (\"selected_background\", None, \"defines the selected background color (#RGB or #RRGGBB)\"),\n        (\"selected_foreground\", None, \"defines the selected foreground color (#RGB or #RRGGBB)\"),\n    ]\n\n    def __init__(self, **config):\n        configurable.Configurable.__init__(self, **config)\n        self.add_defaults(_Extension.defaults)\n        _Extension.installed_extensions.append(self)\n\n    def _check_colors(self):\n        \"\"\"\n        dmenu needs colours to be in #rgb or #rrggbb format.\n\n        Checks colour value, removes invalid values and adds # if missing.\n\n        NB This should not be called in _Extension.__init__ as _Extension.global_defaults\n        may not have been set at this point.\n        \"\"\"\n        for c in [\"background\", \"foreground\", \"selected_background\", \"selected_foreground\"]:\n            col = getattr(self, c, None)\n            if col is None:\n                continue\n\n            if not isinstance(col, str) or not RGB.match(col):\n                logger.warning(\n                    f\"Invalid extension '{c}' color: {col}. \"\n                    f\"Must be #RGB or #RRGGBB string.\"\n                )\n                setattr(self, c, None)\n                continue\n\n            if not col.startswith(\"#\"):\n                col = f\"#{col}\"\n                setattr(self, c, col)\n\n    def _configure(self, qtile):\n        self.qtile = qtile\n        self._check_colors()\n\n    def run(self):\n        \"\"\"\n        This method must be implemented by the subclasses.\n        \"\"\"\n        raise NotImplementedError()\n\n\nclass RunCommand(_Extension):\n    \"\"\"\n    Run an arbitrary command.\n\n    Mostly useful as a superclass for more specific extensions that need to\n    interact with the qtile object.\n\n    Also consider simply using lazy.spawn() or writing a\n    `client <http://docs.qtile.org/en/latest/manual/commands/scripting.html>`_.\n    \"\"\"\n    defaults = [\n        # NOTE: Do not use a list as a default value, since it would be shared\n        #       among all the objects inheriting this class, and if one of them\n        #       modified it, all the other objects would see the modified list;\n        #       use a string or a tuple instead, which are immutable\n        (\"command\", None, \"the command to be launched (string or list with arguments)\"),\n    ]  # type: List[Tuple[str, Any, str]]\n\n    def __init__(self, **config):\n        _Extension.__init__(self, **config)\n        self.add_defaults(RunCommand.defaults)\n        self.configured_command = None\n\n    def run(self):\n        \"\"\"\n        An extension can inherit this class, define configured_command and use\n        the process object by overriding this method and using super():\n\n        .. code-block:: python\n\n            def _configure(self, qtile):\n                Superclass._configure(self, qtile)\n                self.configured_command = \"foo --bar\"\n\n            def run(self):\n                process = super(Subclass, self).run()\n        \"\"\"\n        if self.configured_command:\n            if isinstance(self.configured_command, str):\n                self.configured_command = shlex.split(self.configured_command)\n            # Else assume that self.configured_command is already a sequence\n        else:\n            self.configured_command = self.command\n        return Popen(self.configured_command, stdout=PIPE, stdin=PIPE)\n", "path": "libqtile/extension/base.py"}]}