Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
tbtools.py450 linesDownload Raw Back to debug
1from __future__ import annotations2 3import itertools4import linecache5import os6import re7import sys8import sysconfig9import traceback10import typing as t11 12from markupsafe import escape13 14from ..utils import cached_property15from .console import Console16 17HEADER = """\18<!doctype html>19<html lang=en>20  <head>21    <title>%(title)s // Werkzeug Debugger</title>22    <link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css">23    <link rel="shortcut icon"24        href="?__debugger__=yes&amp;cmd=resource&amp;f=console.png">25    <script src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>26    <script>27      var CONSOLE_MODE = %(console)s,28          EVALEX = %(evalex)s,29          EVALEX_TRUSTED = %(evalex_trusted)s,30          SECRET = "%(secret)s";31    </script>32  </head>33  <body style="background-color: #fff">34    <div class="debugger">35"""36 37FOOTER = """\38      <div class="footer">39        Brought to you by <strong class="arthur">DON'T PANIC</strong>, your40        friendly Werkzeug powered traceback interpreter.41      </div>42    </div>43 44    <div class="pin-prompt">45      <div class="inner">46        <h3>Console Locked</h3>47        <p>48          The console is locked and needs to be unlocked by entering the PIN.49          You can find the PIN printed out on the standard output of your50          shell that runs the server.51        <form>52          <p>PIN:53            <input type=text name=pin size=14>54            <input type=submit name=btn value="Confirm Pin">55        </form>56      </div>57    </div>58  </body>59</html>60"""61 62PAGE_HTML = (63    HEADER64    + """\65<h1>%(exception_type)s</h1>66<div class="detail">67  <p class="errormsg">%(exception)s</p>68</div>69<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>70%(summary)s71<div class="plain">72    <p>73      This is the Copy/Paste friendly version of the traceback.74    </p>75    <textarea cols="50" rows="10" name="code" readonly>%(plaintext)s</textarea>76</div>77<div class="explanation">78  The debugger caught an exception in your WSGI application.  You can now79  look at the traceback which led to the error.  <span class="nojavascript">80  If you enable JavaScript you can also use additional features such as code81  execution (if the evalex feature is enabled), automatic pasting of the82  exceptions and much more.</span>83</div>84"""85    + FOOTER86    + """87<!--88 89%(plaintext_cs)s90 91-->92"""93)94 95CONSOLE_HTML = (96    HEADER97    + """\98<h1>Interactive Console</h1>99<div class="explanation">100In this console you can execute Python expressions in the context of the101application.  The initial namespace was created by the debugger automatically.102</div>103<div class="console"><div class="inner">The Console requires JavaScript.</div></div>104"""105    + FOOTER106)107 108SUMMARY_HTML = """\109<div class="%(classes)s">110  %(title)s111  <ul>%(frames)s</ul>112  %(description)s113</div>114"""115 116FRAME_HTML = """\117<div class="frame" id="frame-%(id)d">118  <h4>File <cite class="filename">"%(filename)s"</cite>,119      line <em class="line">%(lineno)s</em>,120      in <code class="function">%(function_name)s</code></h4>121  <div class="source %(library)s">%(lines)s</div>122</div>123"""124 125 126def _process_traceback(127    exc: BaseException,128    te: traceback.TracebackException | None = None,129    *,130    skip: int = 0,131    hide: bool = True,132) -> traceback.TracebackException:133    if te is None:134        te = traceback.TracebackException.from_exception(exc, lookup_lines=False)135 136    # Get the frames the same way StackSummary.extract did, in order137    # to match each frame with the FrameSummary to augment.138    frame_gen = traceback.walk_tb(exc.__traceback__)139    limit = getattr(sys, "tracebacklimit", None)140 141    if limit is not None:142        if limit < 0:143            limit = 0144 145        frame_gen = itertools.islice(frame_gen, limit)146 147    if skip:148        frame_gen = itertools.islice(frame_gen, skip, None)149        del te.stack[:skip]150 151    new_stack: list[DebugFrameSummary] = []152    hidden = False153 154    # Match each frame with the FrameSummary that was generated.155    # Hide frames using Paste's __traceback_hide__ rules. Replace156    # all visible FrameSummary with DebugFrameSummary.157    for (f, _), fs in zip(frame_gen, te.stack):158        if hide:159            hide_value = f.f_locals.get("__traceback_hide__", False)160 161            if hide_value in {"before", "before_and_this"}:162                new_stack = []163                hidden = False164 165                if hide_value == "before_and_this":166                    continue167            elif hide_value in {"reset", "reset_and_this"}:168                hidden = False169 170                if hide_value == "reset_and_this":171                    continue172            elif hide_value in {"after", "after_and_this"}:173                hidden = True174 175                if hide_value == "after_and_this":176                    continue177            elif hide_value or hidden:178                continue179 180        frame_args: dict[str, t.Any] = {181            "filename": fs.filename,182            "lineno": fs.lineno,183            "name": fs.name,184            "locals": f.f_locals,185            "globals": f.f_globals,186        }187 188        if sys.version_info >= (3, 11):189            frame_args["colno"] = fs.colno190            frame_args["end_colno"] = fs.end_colno191 192        new_stack.append(DebugFrameSummary(**frame_args))193 194    # The codeop module is used to compile code from the interactive195    # debugger. Hide any codeop frames from the bottom of the traceback.196    while new_stack:197        module = new_stack[0].global_ns.get("__name__")198 199        if module is None:200            module = new_stack[0].local_ns.get("__name__")201 202        if module == "codeop":203            del new_stack[0]204        else:205            break206 207    te.stack[:] = new_stack208 209    if te.__context__:210        context_exc = t.cast(BaseException, exc.__context__)211        te.__context__ = _process_traceback(context_exc, te.__context__, hide=hide)212 213    if te.__cause__:214        cause_exc = t.cast(BaseException, exc.__cause__)215        te.__cause__ = _process_traceback(cause_exc, te.__cause__, hide=hide)216 217    return te218 219 220class DebugTraceback:221    __slots__ = ("_te", "_cache_all_tracebacks", "_cache_all_frames")222 223    def __init__(224        self,225        exc: BaseException,226        te: traceback.TracebackException | None = None,227        *,228        skip: int = 0,229        hide: bool = True,230    ) -> None:231        self._te = _process_traceback(exc, te, skip=skip, hide=hide)232 233    def __str__(self) -> str:234        return f"<{type(self).__name__} {self._te}>"235 236    @cached_property237    def all_tracebacks(238        self,239    ) -> list[tuple[str | None, traceback.TracebackException]]:240        out: list[tuple[str | None, traceback.TracebackException]] = []241        current: traceback.TracebackException | None = self._te242 243        while current is not None:244            if current.__cause__ is not None:245                chained_msg = (246                    "The above exception was the direct cause of the"247                    " following exception"248                )249                chained_exc = current.__cause__250            elif current.__context__ is not None and not current.__suppress_context__:251                chained_msg = (252                    "During handling of the above exception, another exception occurred"253                )254                chained_exc = current.__context__255            else:256                chained_msg = None257                chained_exc = None258 259            out.append((chained_msg, current))260            current = chained_exc261 262        return out263 264    @cached_property265    def all_frames(self) -> list[DebugFrameSummary]:266        return [267            f  # type: ignore[misc]268            for _, te in self.all_tracebacks269            for f in te.stack270        ]271 272    def render_traceback_text(self) -> str:273        return "".join(self._te.format())274 275    def render_traceback_html(self, include_title: bool = True) -> str:276        library_frames = [f.is_library for f in self.all_frames]277        mark_library = 0 < sum(library_frames) < len(library_frames)278        rows = []279 280        if not library_frames:281            classes = "traceback noframe-traceback"282        else:283            classes = "traceback"284 285            for msg, current in reversed(self.all_tracebacks):286                row_parts = []287 288                if msg is not None:289                    row_parts.append(f'<li><div class="exc-divider">{msg}:</div>')290 291                for frame in current.stack:292                    frame = t.cast(DebugFrameSummary, frame)293                    info = f' title="{escape(frame.info)}"' if frame.info else ""294                    row_parts.append(f"<li{info}>{frame.render_html(mark_library)}")295 296                rows.append("\n".join(row_parts))297 298        if sys.version_info < (3, 13):299            exc_type_str = self._te.exc_type.__name__300        else:301            exc_type_str = self._te.exc_type_str302 303        is_syntax_error = exc_type_str == "SyntaxError"304 305        if include_title:306            if is_syntax_error:307                title = "Syntax Error"308            else:309                title = "Traceback <em>(most recent call last)</em>:"310        else:311            title = ""312 313        exc_full = escape("".join(self._te.format_exception_only()))314 315        if is_syntax_error:316            description = f"<pre class=syntaxerror>{exc_full}</pre>"317        else:318            description = f"<blockquote>{exc_full}</blockquote>"319 320        return SUMMARY_HTML % {321            "classes": classes,322            "title": f"<h3>{title}</h3>",323            "frames": "\n".join(rows),324            "description": description,325        }326 327    def render_debugger_html(328        self, evalex: bool, secret: str, evalex_trusted: bool329    ) -> str:330        exc_lines = list(self._te.format_exception_only())331        plaintext = "".join(self._te.format())332 333        if sys.version_info < (3, 13):334            exc_type_str = self._te.exc_type.__name__335        else:336            exc_type_str = self._te.exc_type_str337 338        return PAGE_HTML % {339            "evalex": "true" if evalex else "false",340            "evalex_trusted": "true" if evalex_trusted else "false",341            "console": "false",342            "title": escape(exc_lines[0]),343            "exception": escape("".join(exc_lines)),344            "exception_type": escape(exc_type_str),345            "summary": self.render_traceback_html(include_title=False),346            "plaintext": escape(plaintext),347            "plaintext_cs": re.sub("-{2,}", "-", plaintext),348            "secret": secret,349        }350 351 352class DebugFrameSummary(traceback.FrameSummary):353    """A :class:`traceback.FrameSummary` that can evaluate code in the354    frame's namespace.355    """356 357    __slots__ = (358        "local_ns",359        "global_ns",360        "_cache_info",361        "_cache_is_library",362        "_cache_console",363    )364 365    def __init__(366        self,367        *,368        locals: dict[str, t.Any],369        globals: dict[str, t.Any],370        **kwargs: t.Any,371    ) -> None:372        super().__init__(locals=None, **kwargs)373        self.local_ns = locals374        self.global_ns = globals375 376    @cached_property377    def info(self) -> str | None:378        return self.local_ns.get("__traceback_info__")379 380    @cached_property381    def is_library(self) -> bool:382        return any(383            self.filename.startswith((path, os.path.realpath(path)))384            for path in sysconfig.get_paths().values()385        )386 387    @cached_property388    def console(self) -> Console:389        return Console(self.global_ns, self.local_ns)390 391    def eval(self, code: str) -> t.Any:392        return self.console.eval(code)393 394    def render_html(self, mark_library: bool) -> str:395        context = 5396        lines = linecache.getlines(self.filename)397        line_idx = self.lineno - 1  # type: ignore[operator]398        start_idx = max(0, line_idx - context)399        stop_idx = min(len(lines), line_idx + context + 1)400        rendered_lines = []401 402        def render_line(line: str, cls: str) -> None:403            line = line.expandtabs().rstrip()404            stripped_line = line.strip()405            prefix = len(line) - len(stripped_line)406            colno = getattr(self, "colno", 0)407            end_colno = getattr(self, "end_colno", 0)408 409            if cls == "current" and colno and end_colno:410                arrow = (411                    f'\n<span class="ws">{" " * prefix}</span>'412                    f"{' ' * (colno - prefix)}{'^' * (end_colno - colno)}"413                )414            else:415                arrow = ""416 417            rendered_lines.append(418                f'<pre class="line {cls}"><span class="ws">{" " * prefix}</span>'419                f"{escape(stripped_line) if stripped_line else ' '}"420                f"{arrow if arrow else ''}</pre>"421            )422 423        if line_idx < len(lines):424            for line in lines[start_idx:line_idx]:425                render_line(line, "before")426 427            render_line(lines[line_idx], "current")428 429            for line in lines[line_idx + 1 : stop_idx]:430                render_line(line, "after")431 432        return FRAME_HTML % {433            "id": id(self),434            "filename": escape(self.filename),435            "lineno": self.lineno,436            "function_name": escape(self.name),437            "lines": "\n".join(rendered_lines),438            "library": "library" if mark_library and self.is_library else "",439        }440 441 442def render_console_html(secret: str, evalex_trusted: bool) -> str:443    return CONSOLE_HTML % {444        "evalex": "true",445        "evalex_trusted": "true" if evalex_trusted else "false",446        "console": "true",447        "title": "Console",448        "secret": secret,449    }450 
codekingpro/portable-devtools · Team Ai