Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
base.py470 linesDownload Raw Back to grideditor
1import abc2import copy3import os4from collections.abc import Callable5from collections.abc import Container6from collections.abc import Iterable7from collections.abc import MutableSequence8from collections.abc import Sequence9from typing import Any10from typing import ClassVar11from typing import Literal12from typing import overload13 14import urwid15 16import mitmproxy.tools.console.master17from mitmproxy import exceptions18from mitmproxy.tools.console import layoutwidget19from mitmproxy.tools.console import signals20from mitmproxy.utils import strutils21 22 23@overload24def read_file(filename: str, escaped: Literal[True]) -> bytes: ...25 26 27@overload28def read_file(filename: str, escaped: Literal[False]) -> str: ...29 30 31def read_file(filename: str, escaped: bool) -> bytes | str:32    filename = os.path.expanduser(filename)33    try:34        with open(filename, "r" if escaped else "rb") as f:35            d = f.read()36    except OSError as v:37        raise exceptions.CommandError(v)38    if escaped:39        try:40            d = strutils.escaped_str_to_bytes(d)41        except ValueError:42            raise exceptions.CommandError("Invalid Python-style string encoding.")43    return d44 45 46class Cell(urwid.WidgetWrap):47    def get_data(self):48        """49        Raises:50            ValueError, if the current content is invalid.51        """52        raise NotImplementedError()53 54    def selectable(self):55        return True56 57 58class Column(metaclass=abc.ABCMeta):59    subeditor: urwid.Edit = None60 61    def __init__(self, heading):62        self.heading = heading63 64    @abc.abstractmethod65    def Display(self, data) -> Cell:66        pass67 68    @abc.abstractmethod69    def Edit(self, data) -> Cell:70        pass71 72    @abc.abstractmethod73    def blank(self) -> Any:74        pass75 76    def keypress(self, key: str, editor: "GridEditor") -> str | None:77        return key78 79 80class GridRow(urwid.WidgetWrap):81    def __init__(82        self,83        focused: int | None,84        editing: bool,85        editor: "GridEditor",86        values: tuple[Iterable[bytes], Container[int]],87    ) -> None:88        self.focused = focused89        self.editor = editor90        self.edit_col: Cell | None = None91 92        errors = values[1]93        self.fields: Sequence[Any] = []94        for i, v in enumerate(values[0]):95            if focused == i and editing:96                self.edit_col = self.editor.columns[i].Edit(v)97                self.fields.append(self.edit_col)98            else:99                w = self.editor.columns[i].Display(v)100                if focused == i:101                    if i in errors:102                        w = urwid.AttrMap(w, "focusfield_error")103                    else:104                        w = urwid.AttrMap(w, "focusfield")105                elif i in errors:106                    w = urwid.AttrMap(w, "field_error")107                self.fields.append(w)108 109        fspecs = self.fields[:]110        if len(self.fields) > 1:111            fspecs[0] = ("fixed", self.editor.first_width + 2, fspecs[0])112        w = urwid.Columns(fspecs, dividechars=2)113        if focused is not None:114            w.focus_position = focused115        super().__init__(w)116 117    def keypress(self, s, k):118        if self.edit_col:119            w = self._w.column_widths(s)[self.focused]120            k = self.edit_col.keypress((w,), k)121        return k122 123    def selectable(self):124        return True125 126 127class GridWalker(urwid.ListWalker):128    """129    Stores rows as a list of (rows, errors) tuples, where rows is a list130    and errors is a set with an entry of each offset in rows that is an131    error.132    """133 134    def __init__(self, lst: Iterable[list], editor: "GridEditor") -> None:135        self.lst: MutableSequence[tuple[Any, set]] = [(i, set()) for i in lst]136        self.editor = editor137        self.focus = 0138        self.focus_col = 0139        self.edit_row: GridRow | None = None140 141    def _modified(self):142        self.editor.show_empty_msg()143        return super()._modified()144 145    def add_value(self, lst):146        self.lst.append((lst[:], set()))147        self._modified()148 149    def get_current_value(self):150        if self.lst:151            return self.lst[self.focus][0][self.focus_col]152 153    def set_current_value(self, val) -> None:154        errors = self.lst[self.focus][1]155        emsg = self.editor.is_error(self.focus_col, val)156        if emsg:157            signals.status_message.send(message=emsg)158            errors.add(self.focus_col)159        else:160            errors.discard(self.focus_col)161        self.set_value(val, self.focus, self.focus_col, errors)162 163    def set_value(self, val, focus, focus_col, errors=None):164        if not errors:165            errors = set()166        row = list(self.lst[focus][0])167        row[focus_col] = val168        self.lst[focus] = [tuple(row), errors]  # type: ignore169        self._modified()170 171    def delete_focus(self):172        if self.lst:173            del self.lst[self.focus]174            self.focus = min(len(self.lst) - 1, self.focus)175            self._modified()176 177    def _insert(self, pos):178        self.focus = pos179        self.lst.insert(self.focus, ([c.blank() for c in self.editor.columns], set()))180        self.focus_col = 0181        self.start_edit()182 183    def insert(self):184        return self._insert(self.focus)185 186    def add(self):187        return self._insert(min(self.focus + 1, len(self.lst)))188 189    def start_edit(self):190        col = self.editor.columns[self.focus_col]191        if self.lst and not col.subeditor:192            self.edit_row = GridRow(193                self.focus_col, True, self.editor, self.lst[self.focus]194            )195            self._modified()196 197    def stop_edit(self):198        if self.edit_row and self.edit_row.edit_col:199            try:200                val = self.edit_row.edit_col.get_data()201            except ValueError:202                return203            self.edit_row = None204            self.set_current_value(val)205 206    def left(self):207        self.focus_col = max(self.focus_col - 1, 0)208        self._modified()209 210    def right(self):211        self.focus_col = min(self.focus_col + 1, len(self.editor.columns) - 1)212        self._modified()213 214    def tab_next(self):215        self.stop_edit()216        if self.focus_col < len(self.editor.columns) - 1:217            self.focus_col += 1218        elif self.focus != len(self.lst) - 1:219            self.focus_col = 0220            self.focus += 1221        self._modified()222 223    def get_focus(self):224        if self.edit_row:225            return self.edit_row, self.focus226        elif self.lst:227            return (228                GridRow(self.focus_col, False, self.editor, self.lst[self.focus]),229                self.focus,230            )231        else:232            return None, None233 234    def set_focus(self, focus):235        self.stop_edit()236        self.focus = focus237        self._modified()238 239    def get_next(self, pos):240        if pos + 1 >= len(self.lst):241            return None, None242        return GridRow(None, False, self.editor, self.lst[pos + 1]), pos + 1243 244    def get_prev(self, pos):245        if pos - 1 < 0:246            return None, None247        return GridRow(None, False, self.editor, self.lst[pos - 1]), pos - 1248 249 250class GridListBox(urwid.ListBox):251    def __init__(self, lw):252        super().__init__(lw)253 254 255FIRST_WIDTH_MAX = 40256 257 258class BaseGridEditor(urwid.WidgetWrap):259    title: str = ""260    keyctx: ClassVar[str] = "grideditor"261 262    def __init__(263        self,264        master: "mitmproxy.tools.console.master.ConsoleMaster",265        title,266        columns,267        value: Any,268        callback: Callable[..., None],269        *cb_args,270        **cb_kwargs,271    ) -> None:272        value = self.data_in(copy.deepcopy(value))273        self.master = master274        self.title = title275        self.columns = columns276        self.value = value277        self.callback = callback278        self.cb_args = cb_args279        self.cb_kwargs = cb_kwargs280 281        first_width = 20282        if value:283            for r in value:284                assert len(r) == len(self.columns)285                first_width = max(len(r), first_width)286        self.first_width = min(first_width, FIRST_WIDTH_MAX)287 288        h = None289        if any(col.heading for col in self.columns):290            headings = []291            for i, col in enumerate(self.columns):292                c = urwid.Text(col.heading)293                if i == 0 and len(self.columns) > 1:294                    headings.append(("fixed", first_width + 2, c))295                else:296                    headings.append(c)297            h = urwid.Columns(headings, dividechars=2)298            h = urwid.AttrMap(h, "heading")299 300        self.walker = GridWalker(self.value, self)301        self.lb = GridListBox(self.walker)302        w = urwid.Frame(self.lb, header=h)303 304        super().__init__(w)305        self.show_empty_msg()306 307    def layout_popping(self):308        res = []309        for i in self.walker.lst:310            if not i[1] and any([x for x in i[0]]):311                res.append(i[0])312        self.callback(self.data_out(res), *self.cb_args, **self.cb_kwargs)313 314    def show_empty_msg(self):315        if self.walker.lst:316            self._w.footer = None317        else:318            self._w.footer = urwid.Text(319                [320                    ("highlight", "No values - you should add some. Press "),321                    ("key", "?"),322                    ("highlight", " for help."),323                ]324            )325 326    def set_subeditor_value(self, val, focus, focus_col):327        self.walker.set_value(val, focus, focus_col)328 329    def keypress(self, size, key):330        if self.walker.edit_row:331            if key == "esc":332                self.walker.stop_edit()333            elif key == "tab":334                pf, pfc = self.walker.focus, self.walker.focus_col335                self.walker.tab_next()336                if self.walker.focus == pf and self.walker.focus_col != pfc:337                    self.walker.start_edit()338            else:339                self._w.keypress(size, key)340            return None341 342        column = self.columns[self.walker.focus_col]343        if key == "m_start":344            self.walker.set_focus(0)345        elif key == "m_next":346            self.walker.tab_next()347        elif key == "m_end":348            self.walker.set_focus(len(self.walker.lst) - 1)349        elif key == "left":350            self.walker.left()351        elif key == "right":352            self.walker.right()353        elif column.keypress(key, self) and not self.handle_key(key):354            return self._w.keypress(size, key)355 356    def data_out(self, data: Sequence[list]) -> Any:357        """358        Called on raw list data, before data is returned through the359        callback.360        """361        return data362 363    def data_in(self, data: Any) -> Iterable[list]:364        """365        Called to prepare provided data.366        """367        return data368 369    def is_error(self, col: int, val: Any) -> str | None:370        """371        Return None, or a string error message.372        """373        return None374 375    def handle_key(self, key):376        if key == "?":377            signals.pop_view_state.send()378        return False379 380    def cmd_add(self):381        self.walker.add()382 383    def cmd_insert(self):384        self.walker.insert()385 386    def cmd_delete(self):387        self.walker.delete_focus()388 389    def cmd_read_file(self, path):390        self.walker.set_current_value(read_file(path, False))391 392    def cmd_read_file_escaped(self, path):393        self.walker.set_current_value(read_file(path, True))394 395    def cmd_spawn_editor(self):396        o = self.walker.get_current_value()397        if o is not None:398            n = self.master.spawn_editor(o)399            n = strutils.clean_hanging_newline(n)400            self.walker.set_current_value(n)401 402 403class GridEditor(BaseGridEditor):404    title = ""405    columns: Sequence[Column] = ()406    keyctx: ClassVar[str] = "grideditor"407 408    def __init__(409        self,410        master: "mitmproxy.tools.console.master.ConsoleMaster",411        value: Any,412        callback: Callable[..., None],413        *cb_args,414        **cb_kwargs,415    ) -> None:416        super().__init__(417            master, self.title, self.columns, value, callback, *cb_args, **cb_kwargs418        )419 420 421class FocusEditor(urwid.WidgetWrap, layoutwidget.LayoutWidget):422    """423    A specialised GridEditor that edits the current focused flow.424    """425 426    keyctx: ClassVar[str] = "grideditor"427 428    def __init__(self, master):429        self.master = master430 431    def call(self, v, name, *args, **kwargs):432        f = getattr(v, name, None)433        if f:434            f(*args, **kwargs)435 436    def get_data(self, flow):437        """438        Retrieve the data to edit from the current flow.439        """440        raise NotImplementedError441 442    def set_data(self, vals, flow):443        """444        Set the current data on the flow.445        """446        raise NotImplementedError447 448    def set_data_update(self, vals, flow) -> None:449        self.set_data(vals, flow)450        signals.flow_change.send(flow=flow)451 452    def key_responder(self):453        return self._w454 455    def layout_popping(self):456        self.call(self._w, "layout_popping")457 458    def layout_pushed(self, prev):459        if self.master.view.focus.flow:460            self._w = BaseGridEditor(461                self.master,462                self.title,463                self.columns,464                self.get_data(self.master.view.focus.flow),465                self.set_data_update,466                self.master.view.focus.flow,467            )468        else:469            self._w = urwid.Pile([])470 
codekingpro/portable-devtools · Team Ai