aboutsummaryrefslogtreecommitdiffstats
path: root/mitmproxy/tools/console/grideditor/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'mitmproxy/tools/console/grideditor/base.py')
-rw-r--r--mitmproxy/tools/console/grideditor/base.py227
1 files changed, 107 insertions, 120 deletions
diff --git a/mitmproxy/tools/console/grideditor/base.py b/mitmproxy/tools/console/grideditor/base.py
index 35ae655f..cdda3def 100644
--- a/mitmproxy/tools/console/grideditor/base.py
+++ b/mitmproxy/tools/console/grideditor/base.py
@@ -1,26 +1,29 @@
import abc
import copy
-from typing import Any
-from typing import Callable
-from typing import Container
-from typing import Iterable
-from typing import Optional
-from typing import Sequence
-from typing import Tuple
-from typing import Set # noqa
-
+import os
+import typing
import urwid
-from mitmproxy.tools.console import common
+
+from mitmproxy.utils import strutils
+from mitmproxy import exceptions
from mitmproxy.tools.console import signals
+from mitmproxy.tools.console import layoutwidget
import mitmproxy.tools.console.master # noqa
-FOOTER = [
- ('heading_key', "enter"), ":edit ",
- ('heading_key', "q"), ":back ",
-]
-FOOTER_EDITING = [
- ('heading_key', "esc"), ":stop editing ",
-]
+
+def read_file(filename: str, escaped: bool) -> typing.AnyStr:
+ filename = os.path.expanduser(filename)
+ try:
+ with open(filename, "r" if escaped else "rb") as f:
+ d = f.read()
+ except IOError as v:
+ raise exceptions.CommandError(v)
+ if escaped:
+ try:
+ d = strutils.escaped_str_to_bytes(d)
+ except ValueError:
+ raise exceptions.CommandError("Invalid Python-style string encoding.")
+ return d
class Cell(urwid.WidgetWrap):
@@ -50,27 +53,28 @@ class Column(metaclass=abc.ABCMeta):
pass
@abc.abstractmethod
- def blank(self) -> Any:
+ def blank(self) -> typing.Any:
pass
- def keypress(self, key: str, editor: "GridEditor") -> Optional[str]:
+ def keypress(self, key: str, editor: "GridEditor") -> typing.Optional[str]:
return key
class GridRow(urwid.WidgetWrap):
+
def __init__(
self,
- focused: Optional[int],
+ focused: typing.Optional[int],
editing: bool,
editor: "GridEditor",
- values: Tuple[Iterable[bytes], Container[int]]
+ values: typing.Tuple[typing.Iterable[bytes], typing.Container[int]]
) -> None:
self.focused = focused
self.editor = editor
- self.edit_col = None # type: Optional[Cell]
+ self.edit_col = None # type: typing.Optional[Cell]
errors = values[1]
- self.fields = [] # type: Sequence[Any]
+ self.fields = [] # type: typing.Sequence[typing.Any]
for i, v in enumerate(values[0]):
if focused == i and editing:
self.edit_col = self.editor.columns[i].Edit(v)
@@ -116,14 +120,14 @@ class GridWalker(urwid.ListWalker):
def __init__(
self,
- lst: Iterable[list],
+ lst: typing.Iterable[list],
editor: "GridEditor"
) -> None:
- self.lst = [(i, set()) for i in lst] # type: Sequence[Tuple[Any, Set]]
+ self.lst = [(i, set()) for i in lst] # type: typing.Sequence[typing.Tuple[typing.Any, typing.Set]]
self.editor = editor
self.focus = 0
self.focus_col = 0
- self.edit_row = None # type: Optional[GridRow]
+ self.edit_row = None # type: typing.Optional[GridRow]
def _modified(self):
self.editor.show_empty_msg()
@@ -184,12 +188,10 @@ class GridWalker(urwid.ListWalker):
self.edit_row = GridRow(
self.focus_col, True, self.editor, self.lst[self.focus]
)
- signals.footer_help.send(self, helptext=FOOTER_EDITING)
self._modified()
def stop_edit(self):
if self.edit_row:
- signals.footer_help.send(self, helptext=FOOTER)
try:
val = self.edit_row.edit_col.get_data()
except ValueError:
@@ -249,18 +251,19 @@ class GridListBox(urwid.ListBox):
FIRST_WIDTH_MAX = 40
-FIRST_WIDTH_MIN = 20
class BaseGridEditor(urwid.WidgetWrap):
+ title = ""
+ keyctx = "grideditor"
def __init__(
self,
master: "mitmproxy.tools.console.master.ConsoleMaster",
title,
columns,
- value: Any,
- callback: Callable[..., None],
+ value: typing.Any,
+ callback: typing.Callable[..., None],
*cb_args,
**cb_kwargs
) -> None:
@@ -280,36 +283,29 @@ class BaseGridEditor(urwid.WidgetWrap):
first_width = max(len(r), first_width)
self.first_width = min(first_width, FIRST_WIDTH_MAX)
- title = None
- if self.title:
- title = urwid.Text(self.title)
- title = urwid.Padding(title, align="left", width=("relative", 100))
- title = urwid.AttrWrap(title, "heading")
-
- headings = []
- for i, col in enumerate(self.columns):
- c = urwid.Text(col.heading)
- if i == 0 and len(self.columns) > 1:
- headings.append(("fixed", first_width + 2, c))
- else:
- headings.append(c)
- h = urwid.Columns(
- headings,
- dividechars=2
- )
- h = urwid.AttrWrap(h, "heading")
+ h = None
+ if any(col.heading for col in self.columns):
+ headings = []
+ for i, col in enumerate(self.columns):
+ c = urwid.Text(col.heading)
+ if i == 0 and len(self.columns) > 1:
+ headings.append(("fixed", first_width + 2, c))
+ else:
+ headings.append(c)
+ h = urwid.Columns(
+ headings,
+ dividechars=2
+ )
+ h = urwid.AttrWrap(h, "heading")
self.walker = GridWalker(self.value, self)
self.lb = GridListBox(self.walker)
- w = urwid.Frame(
- self.lb,
- header=urwid.Pile([title, h]) if title else None
- )
+ w = urwid.Frame(self.lb, header=h)
+
super().__init__(w)
- signals.footer_help.send(self, helptext="")
self.show_empty_msg()
- def view_popping(self):
+ def layout_popping(self):
res = []
for i in self.walker.lst:
if not i[1] and any([x for x in i[0]]):
@@ -323,9 +319,9 @@ class BaseGridEditor(urwid.WidgetWrap):
self._w.set_footer(
urwid.Text(
[
- ("highlight", "No values. Press "),
- ("key", "a"),
- ("highlight", " to add some."),
+ ("highlight", "No values - you should add some. Press "),
+ ("key", "?"),
+ ("highlight", " for help."),
]
)
)
@@ -335,7 +331,7 @@ class BaseGridEditor(urwid.WidgetWrap):
def keypress(self, size, key):
if self.walker.edit_row:
- if key in ["esc"]:
+ if key == "esc":
self.walker.stop_edit()
elif key == "tab":
pf, pfc = self.walker.focus, self.walker.focus_col
@@ -349,37 +345,31 @@ class BaseGridEditor(urwid.WidgetWrap):
column = self.columns[self.walker.focus_col]
if key == "m_start":
self.walker.set_focus(0)
+ elif key == "m_next":
+ self.walker.tab_next()
elif key == "m_end":
self.walker.set_focus(len(self.walker.lst) - 1)
elif key == "left":
self.walker.left()
elif key == "right":
self.walker.right()
- elif key == "tab":
- self.walker.tab_next()
- elif key == "a":
- self.walker.add()
- elif key == "A":
- self.walker.insert()
- elif key == "d":
- self.walker.delete_focus()
elif column.keypress(key, self) and not self.handle_key(key):
return self._w.keypress(size, key)
- def data_out(self, data: Sequence[list]) -> Any:
+ def data_out(self, data: typing.Sequence[list]) -> typing.Any:
"""
Called on raw list data, before data is returned through the
callback.
"""
return data
- def data_in(self, data: Any) -> Iterable[list]:
+ def data_in(self, data: typing.Any) -> typing.Iterable[list]:
"""
Called to prepare provided data.
"""
return data
- def is_error(self, col: int, val: Any) -> Optional[str]:
+ def is_error(self, col: int, val: typing.Any) -> typing.Optional[str]:
"""
Return None, or a string error message.
"""
@@ -388,60 +378,54 @@ class BaseGridEditor(urwid.WidgetWrap):
def handle_key(self, key):
return False
- def make_help(self):
- text = [
- urwid.Text([("text", "Editor control:\n")])
- ]
- keys = [
- ("A", "insert row before cursor"),
- ("a", "add row after cursor"),
- ("d", "delete row"),
- ("e", "spawn external editor on current field"),
- ("q", "save changes and exit editor"),
- ("r", "read value from file"),
- ("R", "read unescaped value from file"),
- ("esc", "save changes and exit editor"),
- ("tab", "next field"),
- ("enter", "edit field"),
- ]
- text.extend(
- common.format_keyvals(keys, key="key", val="text", indent=4)
- )
- text.append(
- urwid.Text(
- [
- "\n",
- ("text", "Values are escaped Python-style strings.\n"),
- ]
- )
- )
- return text
+ def cmd_add(self):
+ self.walker.add()
+
+ def cmd_insert(self):
+ self.walker.insert()
+
+ def cmd_delete(self):
+ self.walker.delete_focus()
+
+ def cmd_read_file(self, path):
+ self.walker.set_current_value(read_file(path, False))
+
+ def cmd_read_file_escaped(self, path):
+ self.walker.set_current_value(read_file(path, True))
+ def cmd_spawn_editor(self):
+ o = self.walker.get_current_value()
+ if o is not None:
+ n = self.master.spawn_editor(o)
+ n = strutils.clean_hanging_newline(n)
+ self.walker.set_current_value(n)
-class GridEditor(urwid.WidgetWrap):
+
+class GridEditor(BaseGridEditor):
title = None # type: str
- columns = None # type: Sequence[Column]
+ columns = None # type: typing.Sequence[Column]
+ keyctx = "grideditor"
def __init__(
self,
master: "mitmproxy.tools.console.master.ConsoleMaster",
- value: Any,
- callback: Callable[..., None],
+ value: typing.Any,
+ callback: typing.Callable[..., None],
*cb_args,
**cb_kwargs
) -> None:
super().__init__(
master,
- value,
self.title,
self.columns,
+ value,
callback,
*cb_args,
**cb_kwargs
)
-class FocusEditor(urwid.WidgetWrap):
+class FocusEditor(urwid.WidgetWrap, layoutwidget.LayoutWidget):
"""
A specialised GridEditor that edits the current focused flow.
"""
@@ -451,27 +435,11 @@ class FocusEditor(urwid.WidgetWrap):
self.master = master
self.focus_changed()
- def focus_changed(self):
- if self.master.view.focus.flow:
- self._w = BaseGridEditor(
- self.master.view.focus.flow,
- self.title,
- self.columns,
- self.get_data(self.master.view.focus.flow),
- self.set_data_update,
- self.master.view.focus.flow,
- )
- else:
- self._w = urwid.Pile([])
-
def call(self, v, name, *args, **kwargs):
f = getattr(v, name, None)
if f:
f(*args, **kwargs)
- def view_popping(self):
- self.call(self._w, "view_popping")
-
def get_data(self, flow):
"""
Retrieve the data to edit from the current flow.
@@ -487,3 +455,22 @@ class FocusEditor(urwid.WidgetWrap):
def set_data_update(self, vals, flow):
self.set_data(vals, flow)
signals.flow_change.send(self, flow = flow)
+
+ def key_responder(self):
+ return self._w
+
+ def layout_popping(self):
+ self.call(self._w, "layout_popping")
+
+ def focus_changed(self):
+ if self.master.view.focus.flow:
+ self._w = BaseGridEditor(
+ self.master,
+ self.title,
+ self.columns,
+ self.get_data(self.master.view.focus.flow),
+ self.set_data_update,
+ self.master.view.focus.flow,
+ )
+ else:
+ self._w = urwid.Pile([])