We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cmus widget doesn't update when song title has an ampersand ("&")
# Issue description
Whenever a song comes up that has an ampersand in the title, the cmus widget stops updating, even after the song in particular ends. I don't know if it happens with any other symbols, or if it happens when the ampersand is in the artist name. Restarting Qtile fixes the issue as long as the current song does not have an ampersand in its title.

# Qtile version
0.18.1.dev0+g8e7ecc0a.d20210719

# Stack traces
Looking into `~/.local/share/qtile/qtile.log` shows the following

```
2021-07-23 22:20:47,453 ERROR libqtile base.py:on_done():L563 Failed to reschedule.
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 555, in on_done
    self.update(result)
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 467, in update
    self.text = text
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 359, in text
    self.layout.text = self.formatted_text
  File "/usr/lib/python3.9/site-packages/libqtile/drawer.py", line 71, in text
    attrlist, value, accel_char = pangocffi.parse_markup(value)
  File "/usr/lib/python3.9/site-packages/libqtile/pangocffi.py", line 183, in parse_markup
    raise Exception("parse_markup() failed for %s" % value)
Exception: parse_markup() failed for b'\xe2\x99\xab Daisuke Ishiwatari - SHOTGUN&HEAD'
2021-07-23 22:22:22,656 WARNING libqtile lifecycle.py:_atexit():L34 Restarting Qtile with os.execv(...)
2021-07-23 22:23:32,624 ERROR libqtile base.py:on_done():L563 Failed to reschedule.
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 555, in on_done
    self.update(result)
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 467, in update
    self.text = text
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 359, in text
    self.layout.text = self.formatted_text
  File "/usr/lib/python3.9/site-packages/libqtile/drawer.py", line 71, in text
    attrlist, value, accel_char = pangocffi.parse_markup(value)
  File "/usr/lib/python3.9/site-packages/libqtile/pangocffi.py", line 183, in parse_markup
    raise Exception("parse_markup() failed for %s" % value)
Exception: parse_markup() failed for b'\xe2\x99\xab Homestuck - Walk-Stab-Walk (R&E)'
2021-07-23 22:23:32,628 ERROR libqtile base.py:on_done():L563 Failed to reschedule.
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 555, in on_done
    self.update(result)
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 467, in update
    self.text = text
  File "/usr/lib/python3.9/site-packages/libqtile/widget/base.py", line 359, in text
    self.layout.text = self.formatted_text
  File "/usr/lib/python3.9/site-packages/libqtile/drawer.py", line 71, in text
    attrlist, value, accel_char = pangocffi.parse_markup(value)
  File "/usr/lib/python3.9/site-packages/libqtile/pangocffi.py", line 183, in parse_markup
    raise Exception("parse_markup() failed for %s" % value)
Exception: parse_markup() failed for b'\xe2\x99\xab Homestuck - Walk-Stab-Walk (R&E)'
```


--- 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/widget/cmus.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2015, Juan Riquelme González
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 import subprocess
18 from functools import partial
19 
20 from libqtile.widget import base
21 
22 
23 class Cmus(base.ThreadPoolText):
24     """A simple Cmus widget.
25 
26     Show the artist and album of now listening song and allow basic mouse
27     control from the bar:
28 
29         - toggle pause (or play if stopped) on left click;
30         - skip forward in playlist on scroll up;
31         - skip backward in playlist on scroll down.
32 
33     Cmus (https://cmus.github.io) should be installed.
34     """
35     orientations = base.ORIENTATION_HORIZONTAL
36     defaults = [
37         ('play_color', '00ff00', 'Text colour when playing.'),
38         ('noplay_color', 'cecece', 'Text colour when not playing.'),
39         ('update_interval', 0.5, 'Update Time in seconds.')
40     ]
41 
42     def __init__(self, **config):
43         base.ThreadPoolText.__init__(self, "", **config)
44         self.add_defaults(Cmus.defaults)
45         self.status = ""
46         self.local = None
47 
48         self.add_callbacks({
49             'Button1': self.play,
50             'Button4': partial(subprocess.Popen, ['cmus-remote', '-n']),
51             'Button5': partial(subprocess.Popen, ['cmus-remote', '-r']),
52         })
53 
54     def get_info(self):
55         """Return a dictionary with info about the current cmus status."""
56         try:
57             output = self.call_process(['cmus-remote', '-C', 'status'])
58         except subprocess.CalledProcessError as err:
59             output = err.output.decode()
60         if output.startswith("status"):
61             output = output.splitlines()
62             info = {'status': "",
63                     'file': "",
64                     'artist': "",
65                     'album': "",
66                     'title': "",
67                     'stream': ""}
68 
69             for line in output:
70                 for data in info:
71                     if data in line:
72                         index = line.index(data)
73                         if index < 5:
74                             info[data] = line[len(data) + index:].strip()
75                             break
76                     elif line.startswith("set"):
77                         return info
78             return info
79 
80     def now_playing(self):
81         """Return a string with the now playing info (Artist - Song Title)."""
82         info = self.get_info()
83         now_playing = ""
84         if info:
85             status = info['status']
86             if self.status != status:
87                 self.status = status
88                 if self.status == "playing":
89                     self.layout.colour = self.play_color
90                 else:
91                     self.layout.colour = self.noplay_color
92             self.local = info['file'].startswith("/")
93             title = info['title']
94             if self.local:
95                 artist = info['artist']
96                 now_playing = "{0} - {1}".format(artist, title)
97             else:
98                 if info['stream']:
99                     now_playing = info['stream']
100                 else:
101                     now_playing = title
102             if now_playing:
103                 now_playing = "♫ {0}".format(now_playing)
104         return now_playing
105 
106     def play(self):
107         """Play music if stopped, else toggle pause."""
108         if self.status in ('playing', 'paused'):
109             subprocess.Popen(['cmus-remote', '-u'])
110         elif self.status == 'stopped':
111             subprocess.Popen(['cmus-remote', '-p'])
112 
113     def poll(self):
114         """Poll content for the text box."""
115         return self.now_playing()
116
```
--- 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/widget/cmus.py b/libqtile/widget/cmus.py
--- a/libqtile/widget/cmus.py
+++ b/libqtile/widget/cmus.py
@@ -17,6 +17,7 @@
 import subprocess
 from functools import partial
 
+from libqtile import pangocffi
 from libqtile.widget import base
 
 
@@ -101,7 +102,7 @@
                     now_playing = title
             if now_playing:
                 now_playing = "♫ {0}".format(now_playing)
-        return now_playing
+        return pangocffi.markup_escape_text(now_playing)
 
     def play(self):
         """Play music if stopped, else toggle pause."""

{"golden_diff": "diff --git a/libqtile/widget/cmus.py b/libqtile/widget/cmus.py\n--- a/libqtile/widget/cmus.py\n+++ b/libqtile/widget/cmus.py\n@@ -17,6 +17,7 @@\n import subprocess\n from functools import partial\n \n+from libqtile import pangocffi\n from libqtile.widget import base\n \n \n@@ -101,7 +102,7 @@\n                     now_playing = title\n             if now_playing:\n                 now_playing = \"\u266b {0}\".format(now_playing)\n-        return now_playing\n+        return pangocffi.markup_escape_text(now_playing)\n \n     def play(self):\n         \"\"\"Play music if stopped, else toggle pause.\"\"\"\n", "issue": "Cmus widget doesn't update when song title has an ampersand (\"&\")\n# Issue description\r\nWhenever a song comes up that has an ampersand in the title, the cmus widget stops updating, even after the song in particular ends. I don't know if it happens with any other symbols, or if it happens when the ampersand is in the artist name. Restarting Qtile fixes the issue as long as the current song does not have an ampersand in its title.\r\n\r\n# Qtile version\r\n0.18.1.dev0+g8e7ecc0a.d20210719\r\n\r\n# Stack traces\r\nLooking into `~/.local/share/qtile/qtile.log` shows the following\r\n\r\n```\r\n2021-07-23 22:20:47,453 ERROR libqtile base.py:on_done():L563 Failed to reschedule.\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 555, in on_done\r\n    self.update(result)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 467, in update\r\n    self.text = text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 359, in text\r\n    self.layout.text = self.formatted_text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/drawer.py\", line 71, in text\r\n    attrlist, value, accel_char = pangocffi.parse_markup(value)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/pangocffi.py\", line 183, in parse_markup\r\n    raise Exception(\"parse_markup() failed for %s\" % value)\r\nException: parse_markup() failed for b'\\xe2\\x99\\xab Daisuke Ishiwatari - SHOTGUN&HEAD'\r\n2021-07-23 22:22:22,656 WARNING libqtile lifecycle.py:_atexit():L34 Restarting Qtile with os.execv(...)\r\n2021-07-23 22:23:32,624 ERROR libqtile base.py:on_done():L563 Failed to reschedule.\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 555, in on_done\r\n    self.update(result)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 467, in update\r\n    self.text = text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 359, in text\r\n    self.layout.text = self.formatted_text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/drawer.py\", line 71, in text\r\n    attrlist, value, accel_char = pangocffi.parse_markup(value)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/pangocffi.py\", line 183, in parse_markup\r\n    raise Exception(\"parse_markup() failed for %s\" % value)\r\nException: parse_markup() failed for b'\\xe2\\x99\\xab Homestuck - Walk-Stab-Walk (R&E)'\r\n2021-07-23 22:23:32,628 ERROR libqtile base.py:on_done():L563 Failed to reschedule.\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 555, in on_done\r\n    self.update(result)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 467, in update\r\n    self.text = text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/widget/base.py\", line 359, in text\r\n    self.layout.text = self.formatted_text\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/drawer.py\", line 71, in text\r\n    attrlist, value, accel_char = pangocffi.parse_markup(value)\r\n  File \"/usr/lib/python3.9/site-packages/libqtile/pangocffi.py\", line 183, in parse_markup\r\n    raise Exception(\"parse_markup() failed for %s\" % value)\r\nException: parse_markup() failed for b'\\xe2\\x99\\xab Homestuck - Walk-Stab-Walk (R&E)'\r\n```\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (C) 2015, Juan Riquelme Gonz\u00e1lez\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport subprocess\nfrom functools import partial\n\nfrom libqtile.widget import base\n\n\nclass Cmus(base.ThreadPoolText):\n    \"\"\"A simple Cmus widget.\n\n    Show the artist and album of now listening song and allow basic mouse\n    control from the bar:\n\n        - toggle pause (or play if stopped) on left click;\n        - skip forward in playlist on scroll up;\n        - skip backward in playlist on scroll down.\n\n    Cmus (https://cmus.github.io) should be installed.\n    \"\"\"\n    orientations = base.ORIENTATION_HORIZONTAL\n    defaults = [\n        ('play_color', '00ff00', 'Text colour when playing.'),\n        ('noplay_color', 'cecece', 'Text colour when not playing.'),\n        ('update_interval', 0.5, 'Update Time in seconds.')\n    ]\n\n    def __init__(self, **config):\n        base.ThreadPoolText.__init__(self, \"\", **config)\n        self.add_defaults(Cmus.defaults)\n        self.status = \"\"\n        self.local = None\n\n        self.add_callbacks({\n            'Button1': self.play,\n            'Button4': partial(subprocess.Popen, ['cmus-remote', '-n']),\n            'Button5': partial(subprocess.Popen, ['cmus-remote', '-r']),\n        })\n\n    def get_info(self):\n        \"\"\"Return a dictionary with info about the current cmus status.\"\"\"\n        try:\n            output = self.call_process(['cmus-remote', '-C', 'status'])\n        except subprocess.CalledProcessError as err:\n            output = err.output.decode()\n        if output.startswith(\"status\"):\n            output = output.splitlines()\n            info = {'status': \"\",\n                    'file': \"\",\n                    'artist': \"\",\n                    'album': \"\",\n                    'title': \"\",\n                    'stream': \"\"}\n\n            for line in output:\n                for data in info:\n                    if data in line:\n                        index = line.index(data)\n                        if index < 5:\n                            info[data] = line[len(data) + index:].strip()\n                            break\n                    elif line.startswith(\"set\"):\n                        return info\n            return info\n\n    def now_playing(self):\n        \"\"\"Return a string with the now playing info (Artist - Song Title).\"\"\"\n        info = self.get_info()\n        now_playing = \"\"\n        if info:\n            status = info['status']\n            if self.status != status:\n                self.status = status\n                if self.status == \"playing\":\n                    self.layout.colour = self.play_color\n                else:\n                    self.layout.colour = self.noplay_color\n            self.local = info['file'].startswith(\"/\")\n            title = info['title']\n            if self.local:\n                artist = info['artist']\n                now_playing = \"{0} - {1}\".format(artist, title)\n            else:\n                if info['stream']:\n                    now_playing = info['stream']\n                else:\n                    now_playing = title\n            if now_playing:\n                now_playing = \"\u266b {0}\".format(now_playing)\n        return now_playing\n\n    def play(self):\n        \"\"\"Play music if stopped, else toggle pause.\"\"\"\n        if self.status in ('playing', 'paused'):\n            subprocess.Popen(['cmus-remote', '-u'])\n        elif self.status == 'stopped':\n            subprocess.Popen(['cmus-remote', '-p'])\n\n    def poll(self):\n        \"\"\"Poll content for the text box.\"\"\"\n        return self.now_playing()\n", "path": "libqtile/widget/cmus.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (C) 2015, Juan Riquelme Gonz\u00e1lez\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n\nimport subprocess\nfrom functools import partial\n\nfrom libqtile import pangocffi\nfrom libqtile.widget import base\n\n\nclass Cmus(base.ThreadPoolText):\n    \"\"\"A simple Cmus widget.\n\n    Show the artist and album of now listening song and allow basic mouse\n    control from the bar:\n\n        - toggle pause (or play if stopped) on left click;\n        - skip forward in playlist on scroll up;\n        - skip backward in playlist on scroll down.\n\n    Cmus (https://cmus.github.io) should be installed.\n    \"\"\"\n    orientations = base.ORIENTATION_HORIZONTAL\n    defaults = [\n        ('play_color', '00ff00', 'Text colour when playing.'),\n        ('noplay_color', 'cecece', 'Text colour when not playing.'),\n        ('update_interval', 0.5, 'Update Time in seconds.')\n    ]\n\n    def __init__(self, **config):\n        base.ThreadPoolText.__init__(self, \"\", **config)\n        self.add_defaults(Cmus.defaults)\n        self.status = \"\"\n        self.local = None\n\n        self.add_callbacks({\n            'Button1': self.play,\n            'Button4': partial(subprocess.Popen, ['cmus-remote', '-n']),\n            'Button5': partial(subprocess.Popen, ['cmus-remote', '-r']),\n        })\n\n    def get_info(self):\n        \"\"\"Return a dictionary with info about the current cmus status.\"\"\"\n        try:\n            output = self.call_process(['cmus-remote', '-C', 'status'])\n        except subprocess.CalledProcessError as err:\n            output = err.output.decode()\n        if output.startswith(\"status\"):\n            output = output.splitlines()\n            info = {'status': \"\",\n                    'file': \"\",\n                    'artist': \"\",\n                    'album': \"\",\n                    'title': \"\",\n                    'stream': \"\"}\n\n            for line in output:\n                for data in info:\n                    if data in line:\n                        index = line.index(data)\n                        if index < 5:\n                            info[data] = line[len(data) + index:].strip()\n                            break\n                    elif line.startswith(\"set\"):\n                        return info\n            return info\n\n    def now_playing(self):\n        \"\"\"Return a string with the now playing info (Artist - Song Title).\"\"\"\n        info = self.get_info()\n        now_playing = \"\"\n        if info:\n            status = info['status']\n            if self.status != status:\n                self.status = status\n                if self.status == \"playing\":\n                    self.layout.colour = self.play_color\n                else:\n                    self.layout.colour = self.noplay_color\n            self.local = info['file'].startswith(\"/\")\n            title = info['title']\n            if self.local:\n                artist = info['artist']\n                now_playing = \"{0} - {1}\".format(artist, title)\n            else:\n                if info['stream']:\n                    now_playing = info['stream']\n                else:\n                    now_playing = title\n            if now_playing:\n                now_playing = \"\u266b {0}\".format(now_playing)\n        return pangocffi.markup_escape_text(now_playing)\n\n    def play(self):\n        \"\"\"Play music if stopped, else toggle pause.\"\"\"\n        if self.status in ('playing', 'paused'):\n            subprocess.Popen(['cmus-remote', '-u'])\n        elif self.status == 'stopped':\n            subprocess.Popen(['cmus-remote', '-p'])\n\n    def poll(self):\n        \"\"\"Poll content for the text box.\"\"\"\n        return self.now_playing()\n", "path": "libqtile/widget/cmus.py"}]}