Merge pull request #1842 from cortesi/optsave

Save options to file from console & related work
This commit is contained in:
Aldo Cortesi 2016-12-12 12:53:26 +13:00 committed by GitHub
commit 5cf268b012
10 changed files with 80 additions and 37 deletions

View File

@ -212,7 +212,7 @@ class OptManager(metaclass=_DefaultsMeta):
if not text: if not text:
return {} return {}
try: try:
data = ruamel.yaml.load(text, ruamel.yaml.Loader) data = ruamel.yaml.load(text, ruamel.yaml.RoundTripLoader)
except ruamel.yaml.error.YAMLError as v: except ruamel.yaml.error.YAMLError as v:
snip = v.problem_mark.get_snippet() snip = v.problem_mark.get_snippet()
raise exceptions.OptionsError( raise exceptions.OptionsError(

View File

@ -150,9 +150,6 @@ def save_data(path, data):
def ask_save_overwrite(path, data): def ask_save_overwrite(path, data):
if not path:
return
path = os.path.expanduser(path)
if os.path.exists(path): if os.path.exists(path):
def save_overwrite(k): def save_overwrite(k):
if k == "y": if k == "y":

View File

@ -5,8 +5,8 @@ from mitmproxy import flowfilter
from mitmproxy.addons import script from mitmproxy.addons import script
from mitmproxy.tools.console import common from mitmproxy.tools.console import common
from mitmproxy.tools.console.grideditor import base from mitmproxy.tools.console.grideditor import base
from mitmproxy.tools.console.grideditor import col_bytes
from mitmproxy.tools.console.grideditor import col_text from mitmproxy.tools.console.grideditor import col_text
from mitmproxy.tools.console.grideditor import col_bytes
from mitmproxy.tools.console.grideditor import col_subgrid from mitmproxy.tools.console.grideditor import col_subgrid
from mitmproxy.tools.console import signals from mitmproxy.tools.console import signals
from mitmproxy.net.http import user_agents from mitmproxy.net.http import user_agents
@ -74,8 +74,8 @@ class ReplaceEditor(base.GridEditor):
title = "Editing replacement patterns" title = "Editing replacement patterns"
columns = [ columns = [
col_text.Column("Filter"), col_text.Column("Filter"),
col_bytes.Column("Regex"), col_text.Column("Regex"),
col_bytes.Column("Replacement"), col_text.Column("Replacement"),
] ]
def is_error(self, col, val): def is_error(self, col, val):
@ -94,8 +94,8 @@ class SetHeadersEditor(base.GridEditor):
title = "Editing header set patterns" title = "Editing header set patterns"
columns = [ columns = [
col_text.Column("Filter"), col_text.Column("Filter"),
col_bytes.Column("Header"), col_text.Column("Header"),
col_bytes.Column("Value"), col_text.Column("Value"),
] ]
def is_error(self, col, val): def is_error(self, col, val):

View File

@ -386,17 +386,10 @@ class ConsoleMaster(master.Master):
) )
def _write_flows(self, path, flows): def _write_flows(self, path, flows):
if not path: with open(path, "wb") as f:
return
path = os.path.expanduser(path)
try:
f = open(path, "wb")
fw = io.FlowWriter(f) fw = io.FlowWriter(f)
for i in flows: for i in flows:
fw.add(i) fw.add(i)
f.close()
except IOError as v:
signals.status_message.send(message=v.strerror)
def save_one_flow(self, path, flow): def save_one_flow(self, path, flow):
return self._write_flows(path, [flow]) return self._write_flows(path, [flow])
@ -405,8 +398,6 @@ class ConsoleMaster(master.Master):
return self._write_flows(path, self.view) return self._write_flows(path, self.view)
def load_flows_callback(self, path): def load_flows_callback(self, path):
if not path:
return
ret = self.load_flows_path(path) ret = self.load_flows_path(path)
return ret or "Flows loaded from %s" % path return ret or "Flows loaded from %s" % path

View File

@ -9,6 +9,7 @@ from mitmproxy.tools.console import signals
footer = [ footer = [
('heading_key', "enter/space"), ":toggle ", ('heading_key', "enter/space"), ":toggle ",
('heading_key', "C"), ":clear all ", ('heading_key', "C"), ":clear all ",
('heading_key', "W"), ":save ",
] ]
@ -17,6 +18,7 @@ def _mkhelp():
keys = [ keys = [
("enter/space", "activate option"), ("enter/space", "activate option"),
("C", "clear all options"), ("C", "clear all options"),
("w", "save options"),
] ]
text.extend(common.format_keyvals(keys, key="key", val="text", indent=4)) text.extend(common.format_keyvals(keys, key="key", val="text", indent=4))
return text return text
@ -162,8 +164,21 @@ class Options(urwid.WidgetWrap):
if key == "C": if key == "C":
self.clearall() self.clearall()
return None return None
if key == "W":
self.save()
return None
return super().keypress(size, key) return super().keypress(size, key)
def do_save(self, path):
self.master.options.save(path)
return "Saved"
def save(self):
signals.status_prompt_path.send(
prompt = "Save options to file",
callback = self.do_save
)
def clearall(self): def clearall(self):
self.master.options.reset() self.master.options.reset()
signals.update_settings.send(self) signals.update_settings.send(self)

View File

@ -57,8 +57,8 @@ class _PathCompleter:
class PathEdit(urwid.Edit, _PathCompleter): class PathEdit(urwid.Edit, _PathCompleter):
def __init__(self, *args, **kwargs): def __init__(self, prompt, last_path):
urwid.Edit.__init__(self, *args, **kwargs) urwid.Edit.__init__(self, prompt, last_path)
_PathCompleter.__init__(self) _PathCompleter.__init__(self)
def keypress(self, size, key): def keypress(self, size, key):

View File

@ -9,6 +9,28 @@ from mitmproxy.tools.console import signals
from mitmproxy.utils import human from mitmproxy.utils import human
class PromptPath:
def __init__(self, callback, args):
self.callback, self.args = callback, args
def __call__(self, pth):
if not pth:
return
pth = os.path.expanduser(pth)
try:
return self.callback(pth, *self.args)
except IOError as v:
signals.status_message.send(message=v.strerror)
class PromptStub:
def __init__(self, callback, args):
self.callback, self.args = callback, args
def __call__(self, txt):
return self.callback(txt, *self.args)
class ActionBar(urwid.WidgetWrap): class ActionBar(urwid.WidgetWrap):
def __init__(self): def __init__(self):
@ -21,7 +43,8 @@ class ActionBar(urwid.WidgetWrap):
self.last_path = "" self.last_path = ""
self.prompting = False self.prompting = None
self.onekey = False self.onekey = False
self.pathprompt = False self.pathprompt = False
@ -42,7 +65,7 @@ class ActionBar(urwid.WidgetWrap):
def sig_prompt(self, sender, prompt, text, callback, args=()): def sig_prompt(self, sender, prompt, text, callback, args=()):
signals.focus.send(self, section="footer") signals.focus.send(self, section="footer")
self._w = urwid.Edit(self.prep_prompt(prompt), text or "") self._w = urwid.Edit(self.prep_prompt(prompt), text or "")
self.prompting = (callback, args) self.prompting = PromptStub(callback, args)
def sig_path_prompt(self, sender, prompt, callback, args=()): def sig_path_prompt(self, sender, prompt, callback, args=()):
signals.focus.send(self, section="footer") signals.focus.send(self, section="footer")
@ -51,7 +74,7 @@ class ActionBar(urwid.WidgetWrap):
os.path.dirname(self.last_path) os.path.dirname(self.last_path)
) )
self.pathprompt = True self.pathprompt = True
self.prompting = (callback, args) self.prompting = PromptPath(callback, args)
def sig_prompt_onekey(self, sender, prompt, keys, callback, args=()): def sig_prompt_onekey(self, sender, prompt, keys, callback, args=()):
""" """
@ -69,7 +92,7 @@ class ActionBar(urwid.WidgetWrap):
prompt.append(")? ") prompt.append(")? ")
self.onekey = set(i[1] for i in keys) self.onekey = set(i[1] for i in keys)
self._w = urwid.Edit(prompt, "") self._w = urwid.Edit(prompt, "")
self.prompting = (callback, args) self.prompting = PromptStub(callback, args)
def selectable(self): def selectable(self):
return True return True
@ -93,10 +116,10 @@ class ActionBar(urwid.WidgetWrap):
def clear(self): def clear(self):
self._w = urwid.Text("") self._w = urwid.Text("")
self.prompting = False self.prompting = None
def prompt_done(self): def prompt_done(self):
self.prompting = False self.prompting = None
self.onekey = False self.onekey = False
self.pathprompt = False self.pathprompt = False
signals.status_message.send(message="") signals.status_message.send(message="")
@ -105,9 +128,9 @@ class ActionBar(urwid.WidgetWrap):
def prompt_execute(self, txt): def prompt_execute(self, txt):
if self.pathprompt: if self.pathprompt:
self.last_path = txt self.last_path = txt
p, args = self.prompting p = self.prompting
self.prompt_done() self.prompt_done()
msg = p(txt, *args) msg = p(txt)
if msg: if msg:
signals.status_message.send(message=msg, expire=1) signals.status_message.send(message=msg, expire=1)

View File

@ -21,7 +21,7 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
type(value) type(value)
)) ))
if isinstance(typeinfo, typing.UnionMeta): if typeinfo.__qualname__ == "Union":
for T in typeinfo.__union_params__: for T in typeinfo.__union_params__:
try: try:
check_type(attr_name, value, T) check_type(attr_name, value, T)
@ -30,18 +30,24 @@ def check_type(attr_name: str, value: typing.Any, typeinfo: type) -> None:
else: else:
return return
raise e raise e
if isinstance(typeinfo, typing.TupleMeta): elif typeinfo.__qualname__ == "Tuple":
check_type(attr_name, value, tuple) if not isinstance(value, (tuple, list)):
raise e
if len(typeinfo.__tuple_params__) != len(value): if len(typeinfo.__tuple_params__) != len(value):
raise e raise e
for i, (x, T) in enumerate(zip(value, typeinfo.__tuple_params__)): for i, (x, T) in enumerate(zip(value, typeinfo.__tuple_params__)):
check_type("{}[{}]".format(attr_name, i), x, T) check_type("{}[{}]".format(attr_name, i), x, T)
return return
if issubclass(typeinfo, typing.IO): elif typeinfo.__qualname__ == "Sequence":
T = typeinfo.__args__[0]
if not isinstance(value, (tuple, list)):
raise e
for v in value:
check_type(attr_name, v, T)
elif typeinfo.__qualname__ == "IO":
if hasattr(value, "read"): if hasattr(value, "read"):
return return
elif not isinstance(value, typeinfo):
if not isinstance(value, typeinfo):
raise e raise e

View File

@ -54,7 +54,7 @@ class TestPathEdit:
def test_keypress(self): def test_keypress(self):
pe = pathedit.PathEdit() pe = pathedit.PathEdit("", "")
with patch('urwid.widget.Edit.get_edit_text') as get_text, \ with patch('urwid.widget.Edit.get_edit_text') as get_text, \
patch('urwid.widget.Edit.set_edit_text') as set_text: patch('urwid.widget.Edit.set_edit_text') as set_text:

View File

@ -26,6 +26,8 @@ def test_check_type():
typecheck.check_type("foo", 42, str) typecheck.check_type("foo", 42, str)
with pytest.raises(TypeError): with pytest.raises(TypeError):
typecheck.check_type("foo", None, str) typecheck.check_type("foo", None, str)
with pytest.raises(TypeError):
typecheck.check_type("foo", b"foo", str)
def test_check_union(): def test_check_union():
@ -44,5 +46,14 @@ def test_check_tuple():
typecheck.check_type("foo", (42, 42), typing.Tuple[int, str]) typecheck.check_type("foo", (42, 42), typing.Tuple[int, str])
with pytest.raises(TypeError): with pytest.raises(TypeError):
typecheck.check_type("foo", ("42", 42), typing.Tuple[int, str]) typecheck.check_type("foo", ("42", 42), typing.Tuple[int, str])
typecheck.check_type("foo", (42, "42"), typing.Tuple[int, str]) typecheck.check_type("foo", (42, "42"), typing.Tuple[int, str])
def test_check_sequence():
typecheck.check_type("foo", [10], typing.Sequence[int])
with pytest.raises(TypeError):
typecheck.check_type("foo", ["foo"], typing.Sequence[int])
with pytest.raises(TypeError):
typecheck.check_type("foo", [10, "foo"], typing.Sequence[int])
with pytest.raises(TypeError):
typecheck.check_type("foo", [b"foo"], typing.Sequence[str])