We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Body editing is broken.
From @kajojify:

> Enter request-body/response-body editor, then leave it and try to interact with mitmproxy. 
Everything was ok with v3.0.0rc2, but v3.0.1 stops reacting on any button.

I can reproduce this on WSL - this needs to be fixed ASAP and probably warrants a bugfix release. I'm unfortunately super busy this weekend, so it'd be great if someone could take a closer look.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/tools/console/master.py`
Content:
```
1 import mailcap
2 import mimetypes
3 import os
4 import os.path
5 import shlex
6 import signal
7 import stat
8 import subprocess
9 import sys
10 import tempfile
11 import traceback
12 import typing  # noqa
13 
14 import urwid
15 
16 from mitmproxy import addons
17 from mitmproxy import master
18 from mitmproxy import log
19 from mitmproxy.addons import intercept
20 from mitmproxy.addons import eventstore
21 from mitmproxy.addons import readfile
22 from mitmproxy.addons import view
23 from mitmproxy.tools.console import consoleaddons
24 from mitmproxy.tools.console import defaultkeys
25 from mitmproxy.tools.console import keymap
26 from mitmproxy.tools.console import palettes
27 from mitmproxy.tools.console import signals
28 from mitmproxy.tools.console import window
29 
30 
31 class ConsoleMaster(master.Master):
32 
33     def __init__(self, opts):
34         super().__init__(opts)
35 
36         self.start_err = None  # type: typing.Optional[log.LogEntry]
37 
38         self.view = view.View()  # type: view.View
39         self.events = eventstore.EventStore()
40         self.events.sig_add.connect(self.sig_add_log)
41 
42         self.stream_path = None
43         self.keymap = keymap.Keymap(self)
44         defaultkeys.map(self.keymap)
45         self.options.errored.connect(self.options_error)
46 
47         self.view_stack = []
48 
49         signals.call_in.connect(self.sig_call_in)
50         self.addons.add(*addons.default_addons())
51         self.addons.add(
52             intercept.Intercept(),
53             self.view,
54             self.events,
55             consoleaddons.UnsupportedLog(),
56             readfile.ReadFile(),
57             consoleaddons.ConsoleAddon(self),
58         )
59 
60         def sigint_handler(*args, **kwargs):
61             self.prompt_for_exit()
62 
63         signal.signal(signal.SIGINT, sigint_handler)
64 
65         self.window = None
66 
67     def __setattr__(self, name, value):
68         super().__setattr__(name, value)
69         signals.update_settings.send(self)
70 
71     def options_error(self, opts, exc):
72         signals.status_message.send(
73             message=str(exc),
74             expire=1
75         )
76 
77     def prompt_for_exit(self):
78         signals.status_prompt_onekey.send(
79             self,
80             prompt = "Quit",
81             keys = (
82                 ("yes", "y"),
83                 ("no", "n"),
84             ),
85             callback = self.quit,
86         )
87 
88     def sig_add_log(self, event_store, entry: log.LogEntry):
89         if log.log_tier(self.options.verbosity) < log.log_tier(entry.level):
90             return
91         if entry.level in ("error", "warn", "alert"):
92             if self.first_tick:
93                 self.start_err = entry
94             else:
95                 signals.status_message.send(
96                     message=(entry.level, "{}: {}".format(entry.level.title(), entry.msg)),
97                     expire=5
98                 )
99 
100     def sig_call_in(self, sender, seconds, callback, args=()):
101         def cb(*_):
102             return callback(*args)
103         self.loop.set_alarm_in(seconds, cb)
104 
105     def spawn_editor(self, data):
106         text = not isinstance(data, bytes)
107         fd, name = tempfile.mkstemp('', "mproxy", text=text)
108         with open(fd, "w" if text else "wb") as f:
109             f.write(data)
110         # if no EDITOR is set, assume 'vi'
111         c = os.environ.get("EDITOR") or "vi"
112         cmd = shlex.split(c)
113         cmd.append(name)
114         self.ui.stop()
115         try:
116             subprocess.call(cmd)
117         except:
118             signals.status_message.send(
119                 message="Can't start editor: %s" % " ".join(c)
120             )
121         else:
122             with open(name, "r" if text else "rb") as f:
123                 data = f.read()
124         self.ui.start()
125         os.unlink(name)
126         return data
127 
128     def spawn_external_viewer(self, data, contenttype):
129         if contenttype:
130             contenttype = contenttype.split(";")[0]
131             ext = mimetypes.guess_extension(contenttype) or ""
132         else:
133             ext = ""
134         fd, name = tempfile.mkstemp(ext, "mproxy")
135         os.write(fd, data)
136         os.close(fd)
137 
138         # read-only to remind the user that this is a view function
139         os.chmod(name, stat.S_IREAD)
140 
141         cmd = None
142         shell = False
143 
144         if contenttype:
145             c = mailcap.getcaps()
146             cmd, _ = mailcap.findmatch(c, contenttype, filename=name)
147             if cmd:
148                 shell = True
149         if not cmd:
150             # hm which one should get priority?
151             c = os.environ.get("PAGER") or os.environ.get("EDITOR")
152             if not c:
153                 c = "less"
154             cmd = shlex.split(c)
155             cmd.append(name)
156         self.ui.stop()
157         try:
158             subprocess.call(cmd, shell=shell)
159         except:
160             signals.status_message.send(
161                 message="Can't start external viewer: %s" % " ".join(c)
162             )
163         self.ui.start()
164         os.unlink(name)
165 
166     def set_palette(self, opts, updated):
167         self.ui.register_palette(
168             palettes.palettes[opts.console_palette].palette(
169                 opts.console_palette_transparent
170             )
171         )
172         self.ui.clear()
173 
174     def ticker(self, *userdata):
175         changed = self.tick(timeout=0)
176         if changed:
177             self.loop.draw_screen()
178         self.loop.set_alarm_in(0.01, self.ticker)
179 
180     def inject_key(self, key):
181         self.loop.process_input([key])
182 
183     def run(self):
184         if not sys.stdout.isatty():
185             print("Error: mitmproxy's console interface requires a tty. "
186                   "Please run mitmproxy in an interactive shell environment.", file=sys.stderr)
187             sys.exit(1)
188 
189         self.ui = window.Screen()
190         self.ui.set_terminal_properties(256)
191         self.set_palette(self.options, None)
192         self.options.subscribe(
193             self.set_palette,
194             ["console_palette", "console_palette_transparent"]
195         )
196         self.loop = urwid.MainLoop(
197             urwid.SolidFill("x"),
198             screen = self.ui,
199             handle_mouse = self.options.console_mouse,
200         )
201 
202         self.window = window.Window(self)
203         self.loop.widget = self.window
204         self.window.refresh()
205 
206         self.loop.set_alarm_in(0.01, self.ticker)
207 
208         if self.start_err:
209             def display_err(*_):
210                 self.sig_add_log(None, self.start_err)
211                 self.start_err = None
212             self.loop.set_alarm_in(0.01, display_err)
213 
214         self.start()
215         try:
216             self.loop.run()
217         except Exception:
218             self.loop.stop()
219             sys.stdout.flush()
220             print(traceback.format_exc(), file=sys.stderr)
221             print("mitmproxy has crashed!", file=sys.stderr)
222             print("Please lodge a bug report at:", file=sys.stderr)
223             print("\thttps://github.com/mitmproxy/mitmproxy", file=sys.stderr)
224             print("Shutting down...", file=sys.stderr)
225         finally:
226             sys.stderr.flush()
227             super().shutdown()
228 
229     def shutdown(self):
230         raise urwid.ExitMainLoop
231 
232     def overlay(self, widget, **kwargs):
233         self.window.set_overlay(widget, **kwargs)
234 
235     def switch_view(self, name):
236         self.window.push(name)
237 
238     def quit(self, a):
239         if a != "n":
240             self.shutdown()
241
```
--- 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/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py
--- a/mitmproxy/tools/console/master.py
+++ b/mitmproxy/tools/console/master.py
@@ -10,6 +10,7 @@
 import tempfile
 import traceback
 import typing  # noqa
+import contextlib
 
 import urwid
 
@@ -102,6 +103,16 @@
             return callback(*args)
         self.loop.set_alarm_in(seconds, cb)
 
+    @contextlib.contextmanager
+    def uistopped(self):
+        self.loop.stop()
+        try:
+            yield
+        finally:
+            self.loop.start()
+            self.loop.screen_size = None
+            self.loop.draw_screen()
+
     def spawn_editor(self, data):
         text = not isinstance(data, bytes)
         fd, name = tempfile.mkstemp('', "mproxy", text=text)
@@ -111,17 +122,16 @@
         c = os.environ.get("EDITOR") or "vi"
         cmd = shlex.split(c)
         cmd.append(name)
-        self.ui.stop()
-        try:
-            subprocess.call(cmd)
-        except:
-            signals.status_message.send(
-                message="Can't start editor: %s" % " ".join(c)
-            )
-        else:
-            with open(name, "r" if text else "rb") as f:
-                data = f.read()
-        self.ui.start()
+        with self.uistopped():
+            try:
+                subprocess.call(cmd)
+            except:
+                signals.status_message.send(
+                    message="Can't start editor: %s" % " ".join(c)
+                )
+            else:
+                with open(name, "r" if text else "rb") as f:
+                    data = f.read()
         os.unlink(name)
         return data
 
@@ -153,14 +163,13 @@
                 c = "less"
             cmd = shlex.split(c)
             cmd.append(name)
-        self.ui.stop()
-        try:
-            subprocess.call(cmd, shell=shell)
-        except:
-            signals.status_message.send(
-                message="Can't start external viewer: %s" % " ".join(c)
-            )
-        self.ui.start()
+        with self.uistopped():
+            try:
+                subprocess.call(cmd, shell=shell)
+            except:
+                signals.status_message.send(
+                    message="Can't start external viewer: %s" % " ".join(c)
+                )
         os.unlink(name)
 
     def set_palette(self, opts, updated):

{"golden_diff": "diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py\n--- a/mitmproxy/tools/console/master.py\n+++ b/mitmproxy/tools/console/master.py\n@@ -10,6 +10,7 @@\n import tempfile\n import traceback\n import typing  # noqa\n+import contextlib\n \n import urwid\n \n@@ -102,6 +103,16 @@\n             return callback(*args)\n         self.loop.set_alarm_in(seconds, cb)\n \n+    @contextlib.contextmanager\n+    def uistopped(self):\n+        self.loop.stop()\n+        try:\n+            yield\n+        finally:\n+            self.loop.start()\n+            self.loop.screen_size = None\n+            self.loop.draw_screen()\n+\n     def spawn_editor(self, data):\n         text = not isinstance(data, bytes)\n         fd, name = tempfile.mkstemp('', \"mproxy\", text=text)\n@@ -111,17 +122,16 @@\n         c = os.environ.get(\"EDITOR\") or \"vi\"\n         cmd = shlex.split(c)\n         cmd.append(name)\n-        self.ui.stop()\n-        try:\n-            subprocess.call(cmd)\n-        except:\n-            signals.status_message.send(\n-                message=\"Can't start editor: %s\" % \" \".join(c)\n-            )\n-        else:\n-            with open(name, \"r\" if text else \"rb\") as f:\n-                data = f.read()\n-        self.ui.start()\n+        with self.uistopped():\n+            try:\n+                subprocess.call(cmd)\n+            except:\n+                signals.status_message.send(\n+                    message=\"Can't start editor: %s\" % \" \".join(c)\n+                )\n+            else:\n+                with open(name, \"r\" if text else \"rb\") as f:\n+                    data = f.read()\n         os.unlink(name)\n         return data\n \n@@ -153,14 +163,13 @@\n                 c = \"less\"\n             cmd = shlex.split(c)\n             cmd.append(name)\n-        self.ui.stop()\n-        try:\n-            subprocess.call(cmd, shell=shell)\n-        except:\n-            signals.status_message.send(\n-                message=\"Can't start external viewer: %s\" % \" \".join(c)\n-            )\n-        self.ui.start()\n+        with self.uistopped():\n+            try:\n+                subprocess.call(cmd, shell=shell)\n+            except:\n+                signals.status_message.send(\n+                    message=\"Can't start external viewer: %s\" % \" \".join(c)\n+                )\n         os.unlink(name)\n \n     def set_palette(self, opts, updated):\n", "issue": "Body editing is broken.\nFrom @kajojify:\r\n\r\n> Enter request-body/response-body editor, then leave it and try to interact with mitmproxy. \r\nEverything was ok with v3.0.0rc2, but v3.0.1 stops reacting on any button.\r\n\r\nI can reproduce this on WSL - this needs to be fixed ASAP and probably warrants a bugfix release. I'm unfortunately super busy this weekend, so it'd be great if someone could take a closer look.\n", "before_files": [{"content": "import mailcap\nimport mimetypes\nimport os\nimport os.path\nimport shlex\nimport signal\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport traceback\nimport typing  # noqa\n\nimport urwid\n\nfrom mitmproxy import addons\nfrom mitmproxy import master\nfrom mitmproxy import log\nfrom mitmproxy.addons import intercept\nfrom mitmproxy.addons import eventstore\nfrom mitmproxy.addons import readfile\nfrom mitmproxy.addons import view\nfrom mitmproxy.tools.console import consoleaddons\nfrom mitmproxy.tools.console import defaultkeys\nfrom mitmproxy.tools.console import keymap\nfrom mitmproxy.tools.console import palettes\nfrom mitmproxy.tools.console import signals\nfrom mitmproxy.tools.console import window\n\n\nclass ConsoleMaster(master.Master):\n\n    def __init__(self, opts):\n        super().__init__(opts)\n\n        self.start_err = None  # type: typing.Optional[log.LogEntry]\n\n        self.view = view.View()  # type: view.View\n        self.events = eventstore.EventStore()\n        self.events.sig_add.connect(self.sig_add_log)\n\n        self.stream_path = None\n        self.keymap = keymap.Keymap(self)\n        defaultkeys.map(self.keymap)\n        self.options.errored.connect(self.options_error)\n\n        self.view_stack = []\n\n        signals.call_in.connect(self.sig_call_in)\n        self.addons.add(*addons.default_addons())\n        self.addons.add(\n            intercept.Intercept(),\n            self.view,\n            self.events,\n            consoleaddons.UnsupportedLog(),\n            readfile.ReadFile(),\n            consoleaddons.ConsoleAddon(self),\n        )\n\n        def sigint_handler(*args, **kwargs):\n            self.prompt_for_exit()\n\n        signal.signal(signal.SIGINT, sigint_handler)\n\n        self.window = None\n\n    def __setattr__(self, name, value):\n        super().__setattr__(name, value)\n        signals.update_settings.send(self)\n\n    def options_error(self, opts, exc):\n        signals.status_message.send(\n            message=str(exc),\n            expire=1\n        )\n\n    def prompt_for_exit(self):\n        signals.status_prompt_onekey.send(\n            self,\n            prompt = \"Quit\",\n            keys = (\n                (\"yes\", \"y\"),\n                (\"no\", \"n\"),\n            ),\n            callback = self.quit,\n        )\n\n    def sig_add_log(self, event_store, entry: log.LogEntry):\n        if log.log_tier(self.options.verbosity) < log.log_tier(entry.level):\n            return\n        if entry.level in (\"error\", \"warn\", \"alert\"):\n            if self.first_tick:\n                self.start_err = entry\n            else:\n                signals.status_message.send(\n                    message=(entry.level, \"{}: {}\".format(entry.level.title(), entry.msg)),\n                    expire=5\n                )\n\n    def sig_call_in(self, sender, seconds, callback, args=()):\n        def cb(*_):\n            return callback(*args)\n        self.loop.set_alarm_in(seconds, cb)\n\n    def spawn_editor(self, data):\n        text = not isinstance(data, bytes)\n        fd, name = tempfile.mkstemp('', \"mproxy\", text=text)\n        with open(fd, \"w\" if text else \"wb\") as f:\n            f.write(data)\n        # if no EDITOR is set, assume 'vi'\n        c = os.environ.get(\"EDITOR\") or \"vi\"\n        cmd = shlex.split(c)\n        cmd.append(name)\n        self.ui.stop()\n        try:\n            subprocess.call(cmd)\n        except:\n            signals.status_message.send(\n                message=\"Can't start editor: %s\" % \" \".join(c)\n            )\n        else:\n            with open(name, \"r\" if text else \"rb\") as f:\n                data = f.read()\n        self.ui.start()\n        os.unlink(name)\n        return data\n\n    def spawn_external_viewer(self, data, contenttype):\n        if contenttype:\n            contenttype = contenttype.split(\";\")[0]\n            ext = mimetypes.guess_extension(contenttype) or \"\"\n        else:\n            ext = \"\"\n        fd, name = tempfile.mkstemp(ext, \"mproxy\")\n        os.write(fd, data)\n        os.close(fd)\n\n        # read-only to remind the user that this is a view function\n        os.chmod(name, stat.S_IREAD)\n\n        cmd = None\n        shell = False\n\n        if contenttype:\n            c = mailcap.getcaps()\n            cmd, _ = mailcap.findmatch(c, contenttype, filename=name)\n            if cmd:\n                shell = True\n        if not cmd:\n            # hm which one should get priority?\n            c = os.environ.get(\"PAGER\") or os.environ.get(\"EDITOR\")\n            if not c:\n                c = \"less\"\n            cmd = shlex.split(c)\n            cmd.append(name)\n        self.ui.stop()\n        try:\n            subprocess.call(cmd, shell=shell)\n        except:\n            signals.status_message.send(\n                message=\"Can't start external viewer: %s\" % \" \".join(c)\n            )\n        self.ui.start()\n        os.unlink(name)\n\n    def set_palette(self, opts, updated):\n        self.ui.register_palette(\n            palettes.palettes[opts.console_palette].palette(\n                opts.console_palette_transparent\n            )\n        )\n        self.ui.clear()\n\n    def ticker(self, *userdata):\n        changed = self.tick(timeout=0)\n        if changed:\n            self.loop.draw_screen()\n        self.loop.set_alarm_in(0.01, self.ticker)\n\n    def inject_key(self, key):\n        self.loop.process_input([key])\n\n    def run(self):\n        if not sys.stdout.isatty():\n            print(\"Error: mitmproxy's console interface requires a tty. \"\n                  \"Please run mitmproxy in an interactive shell environment.\", file=sys.stderr)\n            sys.exit(1)\n\n        self.ui = window.Screen()\n        self.ui.set_terminal_properties(256)\n        self.set_palette(self.options, None)\n        self.options.subscribe(\n            self.set_palette,\n            [\"console_palette\", \"console_palette_transparent\"]\n        )\n        self.loop = urwid.MainLoop(\n            urwid.SolidFill(\"x\"),\n            screen = self.ui,\n            handle_mouse = self.options.console_mouse,\n        )\n\n        self.window = window.Window(self)\n        self.loop.widget = self.window\n        self.window.refresh()\n\n        self.loop.set_alarm_in(0.01, self.ticker)\n\n        if self.start_err:\n            def display_err(*_):\n                self.sig_add_log(None, self.start_err)\n                self.start_err = None\n            self.loop.set_alarm_in(0.01, display_err)\n\n        self.start()\n        try:\n            self.loop.run()\n        except Exception:\n            self.loop.stop()\n            sys.stdout.flush()\n            print(traceback.format_exc(), file=sys.stderr)\n            print(\"mitmproxy has crashed!\", file=sys.stderr)\n            print(\"Please lodge a bug report at:\", file=sys.stderr)\n            print(\"\\thttps://github.com/mitmproxy/mitmproxy\", file=sys.stderr)\n            print(\"Shutting down...\", file=sys.stderr)\n        finally:\n            sys.stderr.flush()\n            super().shutdown()\n\n    def shutdown(self):\n        raise urwid.ExitMainLoop\n\n    def overlay(self, widget, **kwargs):\n        self.window.set_overlay(widget, **kwargs)\n\n    def switch_view(self, name):\n        self.window.push(name)\n\n    def quit(self, a):\n        if a != \"n\":\n            self.shutdown()\n", "path": "mitmproxy/tools/console/master.py"}], "after_files": [{"content": "import mailcap\nimport mimetypes\nimport os\nimport os.path\nimport shlex\nimport signal\nimport stat\nimport subprocess\nimport sys\nimport tempfile\nimport traceback\nimport typing  # noqa\nimport contextlib\n\nimport urwid\n\nfrom mitmproxy import addons\nfrom mitmproxy import master\nfrom mitmproxy import log\nfrom mitmproxy.addons import intercept\nfrom mitmproxy.addons import eventstore\nfrom mitmproxy.addons import readfile\nfrom mitmproxy.addons import view\nfrom mitmproxy.tools.console import consoleaddons\nfrom mitmproxy.tools.console import defaultkeys\nfrom mitmproxy.tools.console import keymap\nfrom mitmproxy.tools.console import palettes\nfrom mitmproxy.tools.console import signals\nfrom mitmproxy.tools.console import window\n\n\nclass ConsoleMaster(master.Master):\n\n    def __init__(self, opts):\n        super().__init__(opts)\n\n        self.start_err = None  # type: typing.Optional[log.LogEntry]\n\n        self.view = view.View()  # type: view.View\n        self.events = eventstore.EventStore()\n        self.events.sig_add.connect(self.sig_add_log)\n\n        self.stream_path = None\n        self.keymap = keymap.Keymap(self)\n        defaultkeys.map(self.keymap)\n        self.options.errored.connect(self.options_error)\n\n        self.view_stack = []\n\n        signals.call_in.connect(self.sig_call_in)\n        self.addons.add(*addons.default_addons())\n        self.addons.add(\n            intercept.Intercept(),\n            self.view,\n            self.events,\n            consoleaddons.UnsupportedLog(),\n            readfile.ReadFile(),\n            consoleaddons.ConsoleAddon(self),\n        )\n\n        def sigint_handler(*args, **kwargs):\n            self.prompt_for_exit()\n\n        signal.signal(signal.SIGINT, sigint_handler)\n\n        self.window = None\n\n    def __setattr__(self, name, value):\n        super().__setattr__(name, value)\n        signals.update_settings.send(self)\n\n    def options_error(self, opts, exc):\n        signals.status_message.send(\n            message=str(exc),\n            expire=1\n        )\n\n    def prompt_for_exit(self):\n        signals.status_prompt_onekey.send(\n            self,\n            prompt = \"Quit\",\n            keys = (\n                (\"yes\", \"y\"),\n                (\"no\", \"n\"),\n            ),\n            callback = self.quit,\n        )\n\n    def sig_add_log(self, event_store, entry: log.LogEntry):\n        if log.log_tier(self.options.verbosity) < log.log_tier(entry.level):\n            return\n        if entry.level in (\"error\", \"warn\", \"alert\"):\n            if self.first_tick:\n                self.start_err = entry\n            else:\n                signals.status_message.send(\n                    message=(entry.level, \"{}: {}\".format(entry.level.title(), entry.msg)),\n                    expire=5\n                )\n\n    def sig_call_in(self, sender, seconds, callback, args=()):\n        def cb(*_):\n            return callback(*args)\n        self.loop.set_alarm_in(seconds, cb)\n\n    @contextlib.contextmanager\n    def uistopped(self):\n        self.loop.stop()\n        try:\n            yield\n        finally:\n            self.loop.start()\n            self.loop.screen_size = None\n            self.loop.draw_screen()\n\n    def spawn_editor(self, data):\n        text = not isinstance(data, bytes)\n        fd, name = tempfile.mkstemp('', \"mproxy\", text=text)\n        with open(fd, \"w\" if text else \"wb\") as f:\n            f.write(data)\n        # if no EDITOR is set, assume 'vi'\n        c = os.environ.get(\"EDITOR\") or \"vi\"\n        cmd = shlex.split(c)\n        cmd.append(name)\n        with self.uistopped():\n            try:\n                subprocess.call(cmd)\n            except:\n                signals.status_message.send(\n                    message=\"Can't start editor: %s\" % \" \".join(c)\n                )\n            else:\n                with open(name, \"r\" if text else \"rb\") as f:\n                    data = f.read()\n        os.unlink(name)\n        return data\n\n    def spawn_external_viewer(self, data, contenttype):\n        if contenttype:\n            contenttype = contenttype.split(\";\")[0]\n            ext = mimetypes.guess_extension(contenttype) or \"\"\n        else:\n            ext = \"\"\n        fd, name = tempfile.mkstemp(ext, \"mproxy\")\n        os.write(fd, data)\n        os.close(fd)\n\n        # read-only to remind the user that this is a view function\n        os.chmod(name, stat.S_IREAD)\n\n        cmd = None\n        shell = False\n\n        if contenttype:\n            c = mailcap.getcaps()\n            cmd, _ = mailcap.findmatch(c, contenttype, filename=name)\n            if cmd:\n                shell = True\n        if not cmd:\n            # hm which one should get priority?\n            c = os.environ.get(\"PAGER\") or os.environ.get(\"EDITOR\")\n            if not c:\n                c = \"less\"\n            cmd = shlex.split(c)\n            cmd.append(name)\n        with self.uistopped():\n            try:\n                subprocess.call(cmd, shell=shell)\n            except:\n                signals.status_message.send(\n                    message=\"Can't start external viewer: %s\" % \" \".join(c)\n                )\n        os.unlink(name)\n\n    def set_palette(self, opts, updated):\n        self.ui.register_palette(\n            palettes.palettes[opts.console_palette].palette(\n                opts.console_palette_transparent\n            )\n        )\n        self.ui.clear()\n\n    def ticker(self, *userdata):\n        changed = self.tick(timeout=0)\n        if changed:\n            self.loop.draw_screen()\n        self.loop.set_alarm_in(0.01, self.ticker)\n\n    def inject_key(self, key):\n        self.loop.process_input([key])\n\n    def run(self):\n        if not sys.stdout.isatty():\n            print(\"Error: mitmproxy's console interface requires a tty. \"\n                  \"Please run mitmproxy in an interactive shell environment.\", file=sys.stderr)\n            sys.exit(1)\n\n        self.ui = window.Screen()\n        self.ui.set_terminal_properties(256)\n        self.set_palette(self.options, None)\n        self.options.subscribe(\n            self.set_palette,\n            [\"console_palette\", \"console_palette_transparent\"]\n        )\n        self.loop = urwid.MainLoop(\n            urwid.SolidFill(\"x\"),\n            screen = self.ui,\n            handle_mouse = self.options.console_mouse,\n        )\n\n        self.window = window.Window(self)\n        self.loop.widget = self.window\n        self.window.refresh()\n\n        self.loop.set_alarm_in(0.01, self.ticker)\n\n        if self.start_err:\n            def display_err(*_):\n                self.sig_add_log(None, self.start_err)\n                self.start_err = None\n            self.loop.set_alarm_in(0.01, display_err)\n\n        self.start()\n        try:\n            self.loop.run()\n        except Exception:\n            self.loop.stop()\n            sys.stdout.flush()\n            print(traceback.format_exc(), file=sys.stderr)\n            print(\"mitmproxy has crashed!\", file=sys.stderr)\n            print(\"Please lodge a bug report at:\", file=sys.stderr)\n            print(\"\\thttps://github.com/mitmproxy/mitmproxy\", file=sys.stderr)\n            print(\"Shutting down...\", file=sys.stderr)\n        finally:\n            sys.stderr.flush()\n            super().shutdown()\n\n    def shutdown(self):\n        raise urwid.ExitMainLoop\n\n    def overlay(self, widget, **kwargs):\n        self.window.set_overlay(widget, **kwargs)\n\n    def switch_view(self, name):\n        self.window.push(name)\n\n    def quit(self, a):\n        if a != \"n\":\n            self.shutdown()\n", "path": "mitmproxy/tools/console/master.py"}]}