We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Crash when editing str-options
##### Steps to reproduce the problem:

1. Start `mitmproxy` in interactive mode
2. Type `O` to switch to the options view
3. Edit a random `str`-option
4. See `mitmproxy` crash with the following stacktrace:
```
Traceback (most recent call last):
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/master.py", line 86, in run_loop
    loop()
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py", line 286, in run
    self._run()
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py", line 384, in _run
    self.event_loop.run()
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py", line 1484, in run
    reraise(*exc_info)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/compat.py", line 58, in reraise
    raise value
  File "/usr/lib/python3.6/asyncio/events.py", line 145, in _run
    self._callback(*self._args)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/raw_display.py", line 404, in <lambda>
    event_loop, callback, self.get_available_raw_input())
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/raw_display.py", line 502, in parse_input
    callback(processed, processed_codes)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py", line 411, in _update
    self.process_input(keys)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py", line 511, in process_input
    k = self._topmost_widget.keypress(self.screen_size, k)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/window.py", line 309, in keypress
    k = super().keypress(size, k)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/container.py", line 1131, in keypress
    return self.body.keypress( (maxcol, remaining), key )
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/window.py", line 44, in keypress
    ret = super().keypress(size, key)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/container.py", line 1131, in keypress
    return self.body.keypress( (maxcol, remaining), key )
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/options.py", line 283, in keypress
    return self.focus_item.keypress(tsize, key)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/options.py", line 177, in keypress
    d = self.master.options.parse_setval(foc.opt.name, v)
  File "/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/optmanager.py", line 332, in parse_setval
    if o.typespec in (str, typing.Optional[str]):
AttributeError: 'str' object has no attribute 'typespec'

mitmproxy has crashed!
Please lodge a bug report at:
        https://github.com/mitmproxy/mitmproxy
```



##### System information

```
Mitmproxy: 4.0.3
Python:    3.6.6
OpenSSL:   OpenSSL 1.1.0h  27 Mar 2018
Platform:  Linux-4.17.0-x86_64-with-debian-buster-sid
```

--- 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/options.py`
Content:
```
1 import urwid
2 import blinker
3 import textwrap
4 import pprint
5 from typing import Optional, Sequence
6 
7 from mitmproxy import exceptions
8 from mitmproxy import optmanager
9 from mitmproxy.tools.console import layoutwidget
10 from mitmproxy.tools.console import signals
11 from mitmproxy.tools.console import overlay
12 
13 HELP_HEIGHT = 5
14 
15 
16 def can_edit_inplace(opt):
17     if opt.choices:
18         return False
19     if opt.typespec in [str, int, Optional[str], Optional[int]]:
20         return True
21 
22 
23 def fcol(s, width, attr):
24     s = str(s)
25     return (
26         "fixed",
27         width,
28         urwid.Text((attr, s))
29     )
30 
31 
32 option_focus_change = blinker.Signal()
33 
34 
35 class OptionItem(urwid.WidgetWrap):
36     def __init__(self, walker, opt, focused, namewidth, editing):
37         self.walker, self.opt, self.focused = walker, opt, focused
38         self.namewidth = namewidth
39         self.editing = editing
40         super().__init__(None)
41         self._w = self.get_widget()
42 
43     def get_widget(self):
44         val = self.opt.current()
45         if self.opt.typespec == bool:
46             displayval = "true" if val else "false"
47         elif not val:
48             displayval = ""
49         elif self.opt.typespec == Sequence[str]:
50             displayval = pprint.pformat(val, indent=1)
51         else:
52             displayval = str(val)
53 
54         changed = self.walker.master.options.has_changed(self.opt.name)
55         if self.focused:
56             valstyle = "option_active_selected" if changed else "option_selected"
57         else:
58             valstyle = "option_active" if changed else "text"
59 
60         if self.editing:
61             valw = urwid.Edit(edit_text=displayval)
62         else:
63             valw = urwid.AttrMap(
64                 urwid.Padding(
65                     urwid.Text([(valstyle, displayval)])
66                 ),
67                 valstyle
68             )
69 
70         return urwid.Columns(
71             [
72                 (
73                     self.namewidth,
74                     urwid.Text([("title", self.opt.name.ljust(self.namewidth))])
75                 ),
76                 valw
77             ],
78             dividechars=2,
79             focus_column=1
80         )
81 
82     def get_edit_text(self):
83         return self._w[1].get_edit_text()
84 
85     def selectable(self):
86         return True
87 
88     def keypress(self, size, key):
89         if self.editing:
90             self._w[1].keypress(size, key)
91             return
92         return key
93 
94 
95 class OptionListWalker(urwid.ListWalker):
96     def __init__(self, master):
97         self.master = master
98 
99         self.index = 0
100         self.focusobj = None
101 
102         self.opts = sorted(master.options.keys())
103         self.maxlen = max(len(i) for i in self.opts)
104         self.editing = False
105         self.set_focus(0)
106         self.master.options.changed.connect(self.sig_mod)
107 
108     def sig_mod(self, *args, **kwargs):
109         self.opts = sorted(self.master.options.keys())
110         self.maxlen = max(len(i) for i in self.opts)
111         self._modified()
112         self.set_focus(self.index)
113 
114     def start_editing(self):
115         self.editing = True
116         self.focus_obj = self._get(self.index, True)
117         self._modified()
118 
119     def stop_editing(self):
120         self.editing = False
121         self.focus_obj = self._get(self.index, False)
122         self.set_focus(self.index)
123         self._modified()
124 
125     def get_edit_text(self):
126         return self.focus_obj.get_edit_text()
127 
128     def _get(self, pos, editing):
129         name = self.opts[pos]
130         opt = self.master.options._options[name]
131         return OptionItem(
132             self, opt, pos == self.index, self.maxlen, editing
133         )
134 
135     def get_focus(self):
136         return self.focus_obj, self.index
137 
138     def set_focus(self, index):
139         self.editing = False
140         name = self.opts[index]
141         opt = self.master.options._options[name]
142         self.index = index
143         self.focus_obj = self._get(self.index, self.editing)
144         option_focus_change.send(opt.help)
145 
146     def get_next(self, pos):
147         if pos >= len(self.opts) - 1:
148             return None, None
149         pos = pos + 1
150         return self._get(pos, False), pos
151 
152     def get_prev(self, pos):
153         pos = pos - 1
154         if pos < 0:
155             return None, None
156         return self._get(pos, False), pos
157 
158 
159 class OptionsList(urwid.ListBox):
160     def __init__(self, master):
161         self.master = master
162         self.walker = OptionListWalker(master)
163         super().__init__(self.walker)
164 
165     def save_config(self, path):
166         try:
167             optmanager.save(self.master.options, path)
168         except exceptions.OptionsError as e:
169             signals.status_message.send(message=str(e))
170 
171     def keypress(self, size, key):
172         if self.walker.editing:
173             if key == "enter":
174                 foc, idx = self.get_focus()
175                 v = self.walker.get_edit_text()
176                 try:
177                     d = self.master.options.parse_setval(foc.opt.name, v)
178                     self.master.options.update(**{foc.opt.name: d})
179                 except exceptions.OptionsError as v:
180                     signals.status_message.send(message=str(v))
181                 self.walker.stop_editing()
182                 return None
183             elif key == "esc":
184                 self.walker.stop_editing()
185                 return None
186         else:
187             if key == "m_start":
188                 self.set_focus(0)
189                 self.walker._modified()
190             elif key == "m_end":
191                 self.set_focus(len(self.walker.opts) - 1)
192                 self.walker._modified()
193             elif key == "m_select":
194                 foc, idx = self.get_focus()
195                 if foc.opt.typespec == bool:
196                     self.master.options.toggler(foc.opt.name)()
197                     # Bust the focus widget cache
198                     self.set_focus(self.walker.index)
199                 elif can_edit_inplace(foc.opt):
200                     self.walker.start_editing()
201                     self.walker._modified()
202                 elif foc.opt.choices:
203                     self.master.overlay(
204                         overlay.Chooser(
205                             self.master,
206                             foc.opt.name,
207                             foc.opt.choices,
208                             foc.opt.current(),
209                             self.master.options.setter(foc.opt.name)
210                         )
211                     )
212                 elif foc.opt.typespec == Sequence[str]:
213                     self.master.overlay(
214                         overlay.OptionsOverlay(
215                             self.master,
216                             foc.opt.name,
217                             foc.opt.current(),
218                             HELP_HEIGHT + 5
219                         ),
220                         valign="top"
221                     )
222                 else:
223                     raise NotImplementedError()
224         return super().keypress(size, key)
225 
226 
227 class OptionHelp(urwid.Frame):
228     def __init__(self, master):
229         self.master = master
230         super().__init__(self.widget(""))
231         self.set_active(False)
232         option_focus_change.connect(self.sig_mod)
233 
234     def set_active(self, val):
235         h = urwid.Text("Option Help")
236         style = "heading" if val else "heading_inactive"
237         self.header = urwid.AttrWrap(h, style)
238 
239     def widget(self, txt):
240         cols, _ = self.master.ui.get_cols_rows()
241         return urwid.ListBox(
242             [urwid.Text(i) for i in textwrap.wrap(txt, cols)]
243         )
244 
245     def sig_mod(self, txt):
246         self.set_body(self.widget(txt))
247 
248 
249 class Options(urwid.Pile, layoutwidget.LayoutWidget):
250     title = "Options"
251     keyctx = "options"
252 
253     def __init__(self, master):
254         oh = OptionHelp(master)
255         self.optionslist = OptionsList(master)
256         super().__init__(
257             [
258                 self.optionslist,
259                 (HELP_HEIGHT, oh),
260             ]
261         )
262         self.master = master
263 
264     def current_name(self):
265         foc, idx = self.optionslist.get_focus()
266         return foc.opt.name
267 
268     def keypress(self, size, key):
269         if key == "m_next":
270             self.focus_position = (
271                 self.focus_position + 1
272             ) % len(self.widget_list)
273             self.widget_list[1].set_active(self.focus_position == 1)
274             key = None
275 
276         # This is essentially a copypasta from urwid.Pile's keypress handler.
277         # So much for "closed for modification, but open for extension".
278         item_rows = None
279         if len(size) == 2:
280             item_rows = self.get_item_rows(size, focus = True)
281         i = self.widget_list.index(self.focus_item)
282         tsize = self.get_item_size(size, i, True, item_rows)
283         return self.focus_item.keypress(tsize, key)
284
```
--- 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/options.py b/mitmproxy/tools/console/options.py
--- a/mitmproxy/tools/console/options.py
+++ b/mitmproxy/tools/console/options.py
@@ -174,7 +174,7 @@
                 foc, idx = self.get_focus()
                 v = self.walker.get_edit_text()
                 try:
-                    d = self.master.options.parse_setval(foc.opt.name, v)
+                    d = self.master.options.parse_setval(foc.opt, v)
                     self.master.options.update(**{foc.opt.name: d})
                 except exceptions.OptionsError as v:
                     signals.status_message.send(message=str(v))

{"golden_diff": "diff --git a/mitmproxy/tools/console/options.py b/mitmproxy/tools/console/options.py\n--- a/mitmproxy/tools/console/options.py\n+++ b/mitmproxy/tools/console/options.py\n@@ -174,7 +174,7 @@\n                 foc, idx = self.get_focus()\n                 v = self.walker.get_edit_text()\n                 try:\n-                    d = self.master.options.parse_setval(foc.opt.name, v)\n+                    d = self.master.options.parse_setval(foc.opt, v)\n                     self.master.options.update(**{foc.opt.name: d})\n                 except exceptions.OptionsError as v:\n                     signals.status_message.send(message=str(v))\n", "issue": "Crash when editing str-options\n##### Steps to reproduce the problem:\r\n\r\n1. Start `mitmproxy` in interactive mode\r\n2. Type `O` to switch to the options view\r\n3. Edit a random `str`-option\r\n4. See `mitmproxy` crash with the following stacktrace:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/master.py\", line 86, in run_loop\r\n    loop()\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py\", line 286, in run\r\n    self._run()\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py\", line 384, in _run\r\n    self.event_loop.run()\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py\", line 1484, in run\r\n    reraise(*exc_info)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/compat.py\", line 58, in reraise\r\n    raise value\r\n  File \"/usr/lib/python3.6/asyncio/events.py\", line 145, in _run\r\n    self._callback(*self._args)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/raw_display.py\", line 404, in <lambda>\r\n    event_loop, callback, self.get_available_raw_input())\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/raw_display.py\", line 502, in parse_input\r\n    callback(processed, processed_codes)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py\", line 411, in _update\r\n    self.process_input(keys)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/main_loop.py\", line 511, in process_input\r\n    k = self._topmost_widget.keypress(self.screen_size, k)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/window.py\", line 309, in keypress\r\n    k = super().keypress(size, k)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/container.py\", line 1131, in keypress\r\n    return self.body.keypress( (maxcol, remaining), key )\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/window.py\", line 44, in keypress\r\n    ret = super().keypress(size, key)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/urwid/container.py\", line 1131, in keypress\r\n    return self.body.keypress( (maxcol, remaining), key )\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/options.py\", line 283, in keypress\r\n    return self.focus_item.keypress(tsize, key)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/tools/console/options.py\", line 177, in keypress\r\n    d = self.master.options.parse_setval(foc.opt.name, v)\r\n  File \"/tmp/mitmproxy-crash/lib/python3.6/site-packages/mitmproxy/optmanager.py\", line 332, in parse_setval\r\n    if o.typespec in (str, typing.Optional[str]):\r\nAttributeError: 'str' object has no attribute 'typespec'\r\n\r\nmitmproxy has crashed!\r\nPlease lodge a bug report at:\r\n        https://github.com/mitmproxy/mitmproxy\r\n```\r\n\r\n\r\n\r\n##### System information\r\n\r\n```\r\nMitmproxy: 4.0.3\r\nPython:    3.6.6\r\nOpenSSL:   OpenSSL 1.1.0h  27 Mar 2018\r\nPlatform:  Linux-4.17.0-x86_64-with-debian-buster-sid\r\n```\n", "before_files": [{"content": "import urwid\nimport blinker\nimport textwrap\nimport pprint\nfrom typing import Optional, Sequence\n\nfrom mitmproxy import exceptions\nfrom mitmproxy import optmanager\nfrom mitmproxy.tools.console import layoutwidget\nfrom mitmproxy.tools.console import signals\nfrom mitmproxy.tools.console import overlay\n\nHELP_HEIGHT = 5\n\n\ndef can_edit_inplace(opt):\n    if opt.choices:\n        return False\n    if opt.typespec in [str, int, Optional[str], Optional[int]]:\n        return True\n\n\ndef fcol(s, width, attr):\n    s = str(s)\n    return (\n        \"fixed\",\n        width,\n        urwid.Text((attr, s))\n    )\n\n\noption_focus_change = blinker.Signal()\n\n\nclass OptionItem(urwid.WidgetWrap):\n    def __init__(self, walker, opt, focused, namewidth, editing):\n        self.walker, self.opt, self.focused = walker, opt, focused\n        self.namewidth = namewidth\n        self.editing = editing\n        super().__init__(None)\n        self._w = self.get_widget()\n\n    def get_widget(self):\n        val = self.opt.current()\n        if self.opt.typespec == bool:\n            displayval = \"true\" if val else \"false\"\n        elif not val:\n            displayval = \"\"\n        elif self.opt.typespec == Sequence[str]:\n            displayval = pprint.pformat(val, indent=1)\n        else:\n            displayval = str(val)\n\n        changed = self.walker.master.options.has_changed(self.opt.name)\n        if self.focused:\n            valstyle = \"option_active_selected\" if changed else \"option_selected\"\n        else:\n            valstyle = \"option_active\" if changed else \"text\"\n\n        if self.editing:\n            valw = urwid.Edit(edit_text=displayval)\n        else:\n            valw = urwid.AttrMap(\n                urwid.Padding(\n                    urwid.Text([(valstyle, displayval)])\n                ),\n                valstyle\n            )\n\n        return urwid.Columns(\n            [\n                (\n                    self.namewidth,\n                    urwid.Text([(\"title\", self.opt.name.ljust(self.namewidth))])\n                ),\n                valw\n            ],\n            dividechars=2,\n            focus_column=1\n        )\n\n    def get_edit_text(self):\n        return self._w[1].get_edit_text()\n\n    def selectable(self):\n        return True\n\n    def keypress(self, size, key):\n        if self.editing:\n            self._w[1].keypress(size, key)\n            return\n        return key\n\n\nclass OptionListWalker(urwid.ListWalker):\n    def __init__(self, master):\n        self.master = master\n\n        self.index = 0\n        self.focusobj = None\n\n        self.opts = sorted(master.options.keys())\n        self.maxlen = max(len(i) for i in self.opts)\n        self.editing = False\n        self.set_focus(0)\n        self.master.options.changed.connect(self.sig_mod)\n\n    def sig_mod(self, *args, **kwargs):\n        self.opts = sorted(self.master.options.keys())\n        self.maxlen = max(len(i) for i in self.opts)\n        self._modified()\n        self.set_focus(self.index)\n\n    def start_editing(self):\n        self.editing = True\n        self.focus_obj = self._get(self.index, True)\n        self._modified()\n\n    def stop_editing(self):\n        self.editing = False\n        self.focus_obj = self._get(self.index, False)\n        self.set_focus(self.index)\n        self._modified()\n\n    def get_edit_text(self):\n        return self.focus_obj.get_edit_text()\n\n    def _get(self, pos, editing):\n        name = self.opts[pos]\n        opt = self.master.options._options[name]\n        return OptionItem(\n            self, opt, pos == self.index, self.maxlen, editing\n        )\n\n    def get_focus(self):\n        return self.focus_obj, self.index\n\n    def set_focus(self, index):\n        self.editing = False\n        name = self.opts[index]\n        opt = self.master.options._options[name]\n        self.index = index\n        self.focus_obj = self._get(self.index, self.editing)\n        option_focus_change.send(opt.help)\n\n    def get_next(self, pos):\n        if pos >= len(self.opts) - 1:\n            return None, None\n        pos = pos + 1\n        return self._get(pos, False), pos\n\n    def get_prev(self, pos):\n        pos = pos - 1\n        if pos < 0:\n            return None, None\n        return self._get(pos, False), pos\n\n\nclass OptionsList(urwid.ListBox):\n    def __init__(self, master):\n        self.master = master\n        self.walker = OptionListWalker(master)\n        super().__init__(self.walker)\n\n    def save_config(self, path):\n        try:\n            optmanager.save(self.master.options, path)\n        except exceptions.OptionsError as e:\n            signals.status_message.send(message=str(e))\n\n    def keypress(self, size, key):\n        if self.walker.editing:\n            if key == \"enter\":\n                foc, idx = self.get_focus()\n                v = self.walker.get_edit_text()\n                try:\n                    d = self.master.options.parse_setval(foc.opt.name, v)\n                    self.master.options.update(**{foc.opt.name: d})\n                except exceptions.OptionsError as v:\n                    signals.status_message.send(message=str(v))\n                self.walker.stop_editing()\n                return None\n            elif key == \"esc\":\n                self.walker.stop_editing()\n                return None\n        else:\n            if key == \"m_start\":\n                self.set_focus(0)\n                self.walker._modified()\n            elif key == \"m_end\":\n                self.set_focus(len(self.walker.opts) - 1)\n                self.walker._modified()\n            elif key == \"m_select\":\n                foc, idx = self.get_focus()\n                if foc.opt.typespec == bool:\n                    self.master.options.toggler(foc.opt.name)()\n                    # Bust the focus widget cache\n                    self.set_focus(self.walker.index)\n                elif can_edit_inplace(foc.opt):\n                    self.walker.start_editing()\n                    self.walker._modified()\n                elif foc.opt.choices:\n                    self.master.overlay(\n                        overlay.Chooser(\n                            self.master,\n                            foc.opt.name,\n                            foc.opt.choices,\n                            foc.opt.current(),\n                            self.master.options.setter(foc.opt.name)\n                        )\n                    )\n                elif foc.opt.typespec == Sequence[str]:\n                    self.master.overlay(\n                        overlay.OptionsOverlay(\n                            self.master,\n                            foc.opt.name,\n                            foc.opt.current(),\n                            HELP_HEIGHT + 5\n                        ),\n                        valign=\"top\"\n                    )\n                else:\n                    raise NotImplementedError()\n        return super().keypress(size, key)\n\n\nclass OptionHelp(urwid.Frame):\n    def __init__(self, master):\n        self.master = master\n        super().__init__(self.widget(\"\"))\n        self.set_active(False)\n        option_focus_change.connect(self.sig_mod)\n\n    def set_active(self, val):\n        h = urwid.Text(\"Option Help\")\n        style = \"heading\" if val else \"heading_inactive\"\n        self.header = urwid.AttrWrap(h, style)\n\n    def widget(self, txt):\n        cols, _ = self.master.ui.get_cols_rows()\n        return urwid.ListBox(\n            [urwid.Text(i) for i in textwrap.wrap(txt, cols)]\n        )\n\n    def sig_mod(self, txt):\n        self.set_body(self.widget(txt))\n\n\nclass Options(urwid.Pile, layoutwidget.LayoutWidget):\n    title = \"Options\"\n    keyctx = \"options\"\n\n    def __init__(self, master):\n        oh = OptionHelp(master)\n        self.optionslist = OptionsList(master)\n        super().__init__(\n            [\n                self.optionslist,\n                (HELP_HEIGHT, oh),\n            ]\n        )\n        self.master = master\n\n    def current_name(self):\n        foc, idx = self.optionslist.get_focus()\n        return foc.opt.name\n\n    def keypress(self, size, key):\n        if key == \"m_next\":\n            self.focus_position = (\n                self.focus_position + 1\n            ) % len(self.widget_list)\n            self.widget_list[1].set_active(self.focus_position == 1)\n            key = None\n\n        # This is essentially a copypasta from urwid.Pile's keypress handler.\n        # So much for \"closed for modification, but open for extension\".\n        item_rows = None\n        if len(size) == 2:\n            item_rows = self.get_item_rows(size, focus = True)\n        i = self.widget_list.index(self.focus_item)\n        tsize = self.get_item_size(size, i, True, item_rows)\n        return self.focus_item.keypress(tsize, key)\n", "path": "mitmproxy/tools/console/options.py"}], "after_files": [{"content": "import urwid\nimport blinker\nimport textwrap\nimport pprint\nfrom typing import Optional, Sequence\n\nfrom mitmproxy import exceptions\nfrom mitmproxy import optmanager\nfrom mitmproxy.tools.console import layoutwidget\nfrom mitmproxy.tools.console import signals\nfrom mitmproxy.tools.console import overlay\n\nHELP_HEIGHT = 5\n\n\ndef can_edit_inplace(opt):\n    if opt.choices:\n        return False\n    if opt.typespec in [str, int, Optional[str], Optional[int]]:\n        return True\n\n\ndef fcol(s, width, attr):\n    s = str(s)\n    return (\n        \"fixed\",\n        width,\n        urwid.Text((attr, s))\n    )\n\n\noption_focus_change = blinker.Signal()\n\n\nclass OptionItem(urwid.WidgetWrap):\n    def __init__(self, walker, opt, focused, namewidth, editing):\n        self.walker, self.opt, self.focused = walker, opt, focused\n        self.namewidth = namewidth\n        self.editing = editing\n        super().__init__(None)\n        self._w = self.get_widget()\n\n    def get_widget(self):\n        val = self.opt.current()\n        if self.opt.typespec == bool:\n            displayval = \"true\" if val else \"false\"\n        elif not val:\n            displayval = \"\"\n        elif self.opt.typespec == Sequence[str]:\n            displayval = pprint.pformat(val, indent=1)\n        else:\n            displayval = str(val)\n\n        changed = self.walker.master.options.has_changed(self.opt.name)\n        if self.focused:\n            valstyle = \"option_active_selected\" if changed else \"option_selected\"\n        else:\n            valstyle = \"option_active\" if changed else \"text\"\n\n        if self.editing:\n            valw = urwid.Edit(edit_text=displayval)\n        else:\n            valw = urwid.AttrMap(\n                urwid.Padding(\n                    urwid.Text([(valstyle, displayval)])\n                ),\n                valstyle\n            )\n\n        return urwid.Columns(\n            [\n                (\n                    self.namewidth,\n                    urwid.Text([(\"title\", self.opt.name.ljust(self.namewidth))])\n                ),\n                valw\n            ],\n            dividechars=2,\n            focus_column=1\n        )\n\n    def get_edit_text(self):\n        return self._w[1].get_edit_text()\n\n    def selectable(self):\n        return True\n\n    def keypress(self, size, key):\n        if self.editing:\n            self._w[1].keypress(size, key)\n            return\n        return key\n\n\nclass OptionListWalker(urwid.ListWalker):\n    def __init__(self, master):\n        self.master = master\n\n        self.index = 0\n        self.focusobj = None\n\n        self.opts = sorted(master.options.keys())\n        self.maxlen = max(len(i) for i in self.opts)\n        self.editing = False\n        self.set_focus(0)\n        self.master.options.changed.connect(self.sig_mod)\n\n    def sig_mod(self, *args, **kwargs):\n        self.opts = sorted(self.master.options.keys())\n        self.maxlen = max(len(i) for i in self.opts)\n        self._modified()\n        self.set_focus(self.index)\n\n    def start_editing(self):\n        self.editing = True\n        self.focus_obj = self._get(self.index, True)\n        self._modified()\n\n    def stop_editing(self):\n        self.editing = False\n        self.focus_obj = self._get(self.index, False)\n        self.set_focus(self.index)\n        self._modified()\n\n    def get_edit_text(self):\n        return self.focus_obj.get_edit_text()\n\n    def _get(self, pos, editing):\n        name = self.opts[pos]\n        opt = self.master.options._options[name]\n        return OptionItem(\n            self, opt, pos == self.index, self.maxlen, editing\n        )\n\n    def get_focus(self):\n        return self.focus_obj, self.index\n\n    def set_focus(self, index):\n        self.editing = False\n        name = self.opts[index]\n        opt = self.master.options._options[name]\n        self.index = index\n        self.focus_obj = self._get(self.index, self.editing)\n        option_focus_change.send(opt.help)\n\n    def get_next(self, pos):\n        if pos >= len(self.opts) - 1:\n            return None, None\n        pos = pos + 1\n        return self._get(pos, False), pos\n\n    def get_prev(self, pos):\n        pos = pos - 1\n        if pos < 0:\n            return None, None\n        return self._get(pos, False), pos\n\n\nclass OptionsList(urwid.ListBox):\n    def __init__(self, master):\n        self.master = master\n        self.walker = OptionListWalker(master)\n        super().__init__(self.walker)\n\n    def save_config(self, path):\n        try:\n            optmanager.save(self.master.options, path)\n        except exceptions.OptionsError as e:\n            signals.status_message.send(message=str(e))\n\n    def keypress(self, size, key):\n        if self.walker.editing:\n            if key == \"enter\":\n                foc, idx = self.get_focus()\n                v = self.walker.get_edit_text()\n                try:\n                    d = self.master.options.parse_setval(foc.opt, v)\n                    self.master.options.update(**{foc.opt.name: d})\n                except exceptions.OptionsError as v:\n                    signals.status_message.send(message=str(v))\n                self.walker.stop_editing()\n                return None\n            elif key == \"esc\":\n                self.walker.stop_editing()\n                return None\n        else:\n            if key == \"m_start\":\n                self.set_focus(0)\n                self.walker._modified()\n            elif key == \"m_end\":\n                self.set_focus(len(self.walker.opts) - 1)\n                self.walker._modified()\n            elif key == \"m_select\":\n                foc, idx = self.get_focus()\n                if foc.opt.typespec == bool:\n                    self.master.options.toggler(foc.opt.name)()\n                    # Bust the focus widget cache\n                    self.set_focus(self.walker.index)\n                elif can_edit_inplace(foc.opt):\n                    self.walker.start_editing()\n                    self.walker._modified()\n                elif foc.opt.choices:\n                    self.master.overlay(\n                        overlay.Chooser(\n                            self.master,\n                            foc.opt.name,\n                            foc.opt.choices,\n                            foc.opt.current(),\n                            self.master.options.setter(foc.opt.name)\n                        )\n                    )\n                elif foc.opt.typespec == Sequence[str]:\n                    self.master.overlay(\n                        overlay.OptionsOverlay(\n                            self.master,\n                            foc.opt.name,\n                            foc.opt.current(),\n                            HELP_HEIGHT + 5\n                        ),\n                        valign=\"top\"\n                    )\n                else:\n                    raise NotImplementedError()\n        return super().keypress(size, key)\n\n\nclass OptionHelp(urwid.Frame):\n    def __init__(self, master):\n        self.master = master\n        super().__init__(self.widget(\"\"))\n        self.set_active(False)\n        option_focus_change.connect(self.sig_mod)\n\n    def set_active(self, val):\n        h = urwid.Text(\"Option Help\")\n        style = \"heading\" if val else \"heading_inactive\"\n        self.header = urwid.AttrWrap(h, style)\n\n    def widget(self, txt):\n        cols, _ = self.master.ui.get_cols_rows()\n        return urwid.ListBox(\n            [urwid.Text(i) for i in textwrap.wrap(txt, cols)]\n        )\n\n    def sig_mod(self, txt):\n        self.set_body(self.widget(txt))\n\n\nclass Options(urwid.Pile, layoutwidget.LayoutWidget):\n    title = \"Options\"\n    keyctx = \"options\"\n\n    def __init__(self, master):\n        oh = OptionHelp(master)\n        self.optionslist = OptionsList(master)\n        super().__init__(\n            [\n                self.optionslist,\n                (HELP_HEIGHT, oh),\n            ]\n        )\n        self.master = master\n\n    def current_name(self):\n        foc, idx = self.optionslist.get_focus()\n        return foc.opt.name\n\n    def keypress(self, size, key):\n        if key == \"m_next\":\n            self.focus_position = (\n                self.focus_position + 1\n            ) % len(self.widget_list)\n            self.widget_list[1].set_active(self.focus_position == 1)\n            key = None\n\n        # This is essentially a copypasta from urwid.Pile's keypress handler.\n        # So much for \"closed for modification, but open for extension\".\n        item_rows = None\n        if len(size) == 2:\n            item_rows = self.get_item_rows(size, focus = True)\n        i = self.widget_list.index(self.focus_item)\n        tsize = self.get_item_size(size, i, True, item_rows)\n        return self.focus_item.keypress(tsize, key)\n", "path": "mitmproxy/tools/console/options.py"}]}