2025-12-25 upload
This commit is contained in:
277
venv/Lib/site-packages/mitmproxy/tools/console/options.py
Normal file
277
venv/Lib/site-packages/mitmproxy/tools/console/options.py
Normal file
@@ -0,0 +1,277 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pprint
|
||||
import textwrap
|
||||
import typing
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
import urwid
|
||||
|
||||
from mitmproxy import exceptions
|
||||
from mitmproxy import optmanager
|
||||
from mitmproxy.tools.console import layoutwidget
|
||||
from mitmproxy.tools.console import overlay
|
||||
from mitmproxy.tools.console import signals
|
||||
|
||||
HELP_HEIGHT = 5
|
||||
|
||||
|
||||
def can_edit_inplace(opt):
|
||||
if opt.choices:
|
||||
return False
|
||||
if opt.typespec in [str, int, Optional[str], Optional[int]]:
|
||||
return True
|
||||
|
||||
|
||||
def fcol(s, width, attr):
|
||||
s = str(s)
|
||||
return ("fixed", width, urwid.Text((attr, s)))
|
||||
|
||||
|
||||
class OptionItem(urwid.WidgetWrap):
|
||||
def __init__(self, walker, opt, focused, namewidth, editing):
|
||||
self.walker, self.opt, self.focused = walker, opt, focused
|
||||
self.namewidth = namewidth
|
||||
self.editing = editing
|
||||
super().__init__(self.get_widget())
|
||||
|
||||
def get_widget(self):
|
||||
val = self.opt.current()
|
||||
if self.opt.typespec is bool:
|
||||
displayval = "true" if val else "false"
|
||||
elif not val:
|
||||
displayval = ""
|
||||
elif self.opt.typespec == Sequence[str]:
|
||||
displayval = pprint.pformat(val, indent=1)
|
||||
else:
|
||||
displayval = str(val)
|
||||
|
||||
changed = self.walker.master.options.has_changed(self.opt.name)
|
||||
if self.focused:
|
||||
valstyle = "option_active_selected" if changed else "option_selected"
|
||||
else:
|
||||
valstyle = "option_active" if changed else "text"
|
||||
|
||||
if self.editing:
|
||||
valw = urwid.Edit(edit_text=displayval)
|
||||
else:
|
||||
valw = urwid.AttrMap(
|
||||
urwid.Padding(urwid.Text([(valstyle, displayval)])), valstyle
|
||||
)
|
||||
|
||||
return urwid.Columns(
|
||||
[
|
||||
(
|
||||
self.namewidth,
|
||||
urwid.Text([("title", self.opt.name.ljust(self.namewidth))]),
|
||||
),
|
||||
valw,
|
||||
],
|
||||
dividechars=2,
|
||||
focus_column=1,
|
||||
)
|
||||
|
||||
def get_edit_text(self):
|
||||
return self._w[1].get_edit_text()
|
||||
|
||||
def selectable(self):
|
||||
return True
|
||||
|
||||
def keypress(self, size, key):
|
||||
if self.editing:
|
||||
self._w[1].keypress(size, key)
|
||||
return
|
||||
return key
|
||||
|
||||
|
||||
class OptionListWalker(urwid.ListWalker):
|
||||
def __init__(self, master, help_widget: OptionHelp):
|
||||
self.master = master
|
||||
self.help_widget = help_widget
|
||||
|
||||
self.index = 0
|
||||
self.focusobj = None
|
||||
|
||||
self.opts = sorted(master.options.keys())
|
||||
self.maxlen = max(len(i) for i in self.opts)
|
||||
self.editing = False
|
||||
self.set_focus(0)
|
||||
self.master.options.changed.connect(self.sig_mod)
|
||||
|
||||
def sig_mod(self, *args, **kwargs):
|
||||
self.opts = sorted(self.master.options.keys())
|
||||
self.maxlen = max(len(i) for i in self.opts)
|
||||
self._modified()
|
||||
self.set_focus(self.index)
|
||||
|
||||
def start_editing(self):
|
||||
self.editing = True
|
||||
self.focus_obj = self._get(self.index, True)
|
||||
self._modified()
|
||||
|
||||
def stop_editing(self):
|
||||
self.editing = False
|
||||
self.focus_obj = self._get(self.index, False)
|
||||
self.set_focus(self.index)
|
||||
self._modified()
|
||||
|
||||
def get_edit_text(self):
|
||||
return self.focus_obj.get_edit_text()
|
||||
|
||||
def _get(self, pos, editing):
|
||||
name = self.opts[pos]
|
||||
opt = self.master.options._options[name]
|
||||
return OptionItem(self, opt, pos == self.index, self.maxlen, editing)
|
||||
|
||||
def get_focus(self):
|
||||
return self.focus_obj, self.index
|
||||
|
||||
def set_focus(self, index):
|
||||
self.editing = False
|
||||
name = self.opts[index]
|
||||
opt = self.master.options._options[name]
|
||||
self.index = index
|
||||
self.focus_obj = self._get(self.index, self.editing)
|
||||
self.help_widget.update_help_text(opt.help)
|
||||
self._modified()
|
||||
|
||||
def get_next(self, pos):
|
||||
if pos >= len(self.opts) - 1:
|
||||
return None, None
|
||||
pos = pos + 1
|
||||
return self._get(pos, False), pos
|
||||
|
||||
def get_prev(self, pos):
|
||||
pos = pos - 1
|
||||
if pos < 0:
|
||||
return None, None
|
||||
return self._get(pos, False), pos
|
||||
|
||||
def positions(self, reverse=False):
|
||||
if reverse:
|
||||
return reversed(range(len(self.opts)))
|
||||
else:
|
||||
return range(len(self.opts))
|
||||
|
||||
|
||||
class OptionsList(urwid.ListBox):
|
||||
def __init__(self, master, help_widget: OptionHelp):
|
||||
self.master = master
|
||||
self.walker = OptionListWalker(master, help_widget)
|
||||
super().__init__(self.walker)
|
||||
|
||||
def save_config(self, path):
|
||||
try:
|
||||
optmanager.save(self.master.options, path)
|
||||
except exceptions.OptionsError as e:
|
||||
signals.status_message.send(message=str(e))
|
||||
|
||||
def keypress(self, size, key):
|
||||
if self.walker.editing:
|
||||
if key == "enter":
|
||||
foc, idx = self.get_focus()
|
||||
v = self.walker.get_edit_text()
|
||||
try:
|
||||
self.master.options.set(f"{foc.opt.name}={v}")
|
||||
except exceptions.OptionsError as v:
|
||||
signals.status_message.send(message=str(v))
|
||||
self.walker.stop_editing()
|
||||
return None
|
||||
elif key == "esc":
|
||||
self.walker.stop_editing()
|
||||
return None
|
||||
else:
|
||||
if key == "m_start":
|
||||
self.set_focus(0)
|
||||
self.walker._modified()
|
||||
elif key == "m_end":
|
||||
self.set_focus(len(self.walker.opts) - 1)
|
||||
self.walker._modified()
|
||||
elif key == "m_select":
|
||||
foc, idx = self.get_focus()
|
||||
if foc.opt.typespec is bool:
|
||||
self.master.options.toggler(foc.opt.name)()
|
||||
# Bust the focus widget cache
|
||||
self.set_focus(self.walker.index)
|
||||
elif can_edit_inplace(foc.opt):
|
||||
self.walker.start_editing()
|
||||
self.walker._modified()
|
||||
elif foc.opt.choices:
|
||||
self.master.overlay(
|
||||
overlay.Chooser(
|
||||
self.master,
|
||||
foc.opt.name,
|
||||
foc.opt.choices,
|
||||
foc.opt.current(),
|
||||
self.master.options.setter(foc.opt.name),
|
||||
)
|
||||
)
|
||||
elif foc.opt.typespec in (Sequence[str], typing.Sequence[str]):
|
||||
self.master.overlay(
|
||||
overlay.OptionsOverlay(
|
||||
self.master,
|
||||
foc.opt.name,
|
||||
foc.opt.current(),
|
||||
HELP_HEIGHT + 5,
|
||||
),
|
||||
valign="top",
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
return super().keypress(size, key)
|
||||
|
||||
|
||||
class OptionHelp(urwid.Frame):
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
super().__init__(self.widget(""))
|
||||
self.set_active(False)
|
||||
|
||||
def set_active(self, val):
|
||||
h = urwid.Text("Option Help")
|
||||
style = "heading" if val else "heading_inactive"
|
||||
self.header = urwid.AttrMap(h, style)
|
||||
|
||||
def widget(self, txt):
|
||||
cols, _ = self.master.ui.get_cols_rows()
|
||||
return urwid.ListBox([urwid.Text(i) for i in textwrap.wrap(txt, cols)])
|
||||
|
||||
def update_help_text(self, txt: str) -> None:
|
||||
self.body = self.widget(txt)
|
||||
|
||||
|
||||
class Options(urwid.Pile, layoutwidget.LayoutWidget):
|
||||
title = "Options"
|
||||
keyctx = "options"
|
||||
|
||||
focus_position: int
|
||||
|
||||
def __init__(self, master):
|
||||
oh = OptionHelp(master)
|
||||
self.optionslist = OptionsList(master, oh)
|
||||
super().__init__(
|
||||
[
|
||||
self.optionslist,
|
||||
(HELP_HEIGHT, oh),
|
||||
]
|
||||
)
|
||||
self.master = master
|
||||
|
||||
def current_name(self):
|
||||
foc, idx = self.optionslist.get_focus()
|
||||
return foc.opt.name
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key == "m_next":
|
||||
self.focus_position = (self.focus_position + 1) % len(self.widget_list)
|
||||
self.widget_list[1].set_active(self.focus_position == 1)
|
||||
key = None
|
||||
|
||||
# This is essentially a copypasta from urwid.Pile's keypress handler.
|
||||
# So much for "closed for modification, but open for extension".
|
||||
item_rows = None
|
||||
if len(size) == 2:
|
||||
item_rows = self.get_item_rows(size, focus=True)
|
||||
tsize = self.get_item_size(size, self.focus_position, True, item_rows)
|
||||
return self.focus.keypress(tsize, key)
|
||||
Reference in New Issue
Block a user