Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
traceback.py754 linesDownload Raw Back to rich
1from __future__ import absolute_import2 3import linecache4import os5import platform6import sys7from dataclasses import dataclass, field8from traceback import walk_tb9from types import ModuleType, TracebackType10from typing import (11    Any,12    Callable,13    Dict,14    Iterable,15    List,16    Optional,17    Sequence,18    Tuple,19    Type,20    Union,21)22 23from pygments.lexers import guess_lexer_for_filename24from pygments.token import Comment, Keyword, Name, Number, Operator, String25from pygments.token import Text as TextToken26from pygments.token import Token27from pygments.util import ClassNotFound28 29from . import pretty30from ._loop import loop_last31from .columns import Columns32from .console import Console, ConsoleOptions, ConsoleRenderable, RenderResult, group33from .constrain import Constrain34from .highlighter import RegexHighlighter, ReprHighlighter35from .panel import Panel36from .scope import render_scope37from .style import Style38from .syntax import Syntax39from .text import Text40from .theme import Theme41 42WINDOWS = platform.system() == "Windows"43 44LOCALS_MAX_LENGTH = 1045LOCALS_MAX_STRING = 8046 47 48def install(49    *,50    console: Optional[Console] = None,51    width: Optional[int] = 100,52    extra_lines: int = 3,53    theme: Optional[str] = None,54    word_wrap: bool = False,55    show_locals: bool = False,56    locals_max_length: int = LOCALS_MAX_LENGTH,57    locals_max_string: int = LOCALS_MAX_STRING,58    locals_hide_dunder: bool = True,59    locals_hide_sunder: Optional[bool] = None,60    indent_guides: bool = True,61    suppress: Iterable[Union[str, ModuleType]] = (),62    max_frames: int = 100,63) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]:64    """Install a rich traceback handler.65 66    Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.67 68 69    Args:70        console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance.71        width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100.72        extra_lines (int, optional): Extra lines of code. Defaults to 3.73        theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick74            a theme appropriate for the platform.75        word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.76        show_locals (bool, optional): Enable display of local variables. Defaults to False.77        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.78            Defaults to 10.79        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.80        locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.81        locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.82        indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.83        suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.84 85    Returns:86        Callable: The previous exception handler that was replaced.87 88    """89    traceback_console = Console(stderr=True) if console is None else console90 91    locals_hide_sunder = (92        True93        if (traceback_console.is_jupyter and locals_hide_sunder is None)94        else locals_hide_sunder95    )96 97    def excepthook(98        type_: Type[BaseException],99        value: BaseException,100        traceback: Optional[TracebackType],101    ) -> None:102        traceback_console.print(103            Traceback.from_exception(104                type_,105                value,106                traceback,107                width=width,108                extra_lines=extra_lines,109                theme=theme,110                word_wrap=word_wrap,111                show_locals=show_locals,112                locals_max_length=locals_max_length,113                locals_max_string=locals_max_string,114                locals_hide_dunder=locals_hide_dunder,115                locals_hide_sunder=bool(locals_hide_sunder),116                indent_guides=indent_guides,117                suppress=suppress,118                max_frames=max_frames,119            )120        )121 122    def ipy_excepthook_closure(ip: Any) -> None:  # pragma: no cover123        tb_data = {}  # store information about showtraceback call124        default_showtraceback = ip.showtraceback  # keep reference of default traceback125 126        def ipy_show_traceback(*args: Any, **kwargs: Any) -> None:127            """wrap the default ip.showtraceback to store info for ip._showtraceback"""128            nonlocal tb_data129            tb_data = kwargs130            default_showtraceback(*args, **kwargs)131 132        def ipy_display_traceback(133            *args: Any, is_syntax: bool = False, **kwargs: Any134        ) -> None:135            """Internally called traceback from ip._showtraceback"""136            nonlocal tb_data137            exc_tuple = ip._get_exc_info()138 139            # do not display trace on syntax error140            tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]141 142            # determine correct tb_offset143            compiled = tb_data.get("running_compiled_code", False)144            tb_offset = tb_data.get("tb_offset", 1 if compiled else 0)145            # remove ipython internal frames from trace with tb_offset146            for _ in range(tb_offset):147                if tb is None:148                    break149                tb = tb.tb_next150 151            excepthook(exc_tuple[0], exc_tuple[1], tb)152            tb_data = {}  # clear data upon usage153 154        # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work155        # this is also what the ipython docs recommends to modify when subclassing InteractiveShell156        ip._showtraceback = ipy_display_traceback157        # add wrapper to capture tb_data158        ip.showtraceback = ipy_show_traceback159        ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(160            *args, is_syntax=True, **kwargs161        )162 163    try:  # pragma: no cover164        # if within ipython, use customized traceback165        ip = get_ipython()  # type: ignore[name-defined]166        ipy_excepthook_closure(ip)167        return sys.excepthook168    except Exception:169        # otherwise use default system hook170        old_excepthook = sys.excepthook171        sys.excepthook = excepthook172        return old_excepthook173 174 175@dataclass176class Frame:177    filename: str178    lineno: int179    name: str180    line: str = ""181    locals: Optional[Dict[str, pretty.Node]] = None182 183 184@dataclass185class _SyntaxError:186    offset: int187    filename: str188    line: str189    lineno: int190    msg: str191 192 193@dataclass194class Stack:195    exc_type: str196    exc_value: str197    syntax_error: Optional[_SyntaxError] = None198    is_cause: bool = False199    frames: List[Frame] = field(default_factory=list)200 201 202@dataclass203class Trace:204    stacks: List[Stack]205 206 207class PathHighlighter(RegexHighlighter):208    highlights = [r"(?P<dim>.*/)(?P<bold>.+)"]209 210 211class Traceback:212    """A Console renderable that renders a traceback.213 214    Args:215        trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses216            the last exception.217        width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.218        extra_lines (int, optional): Additional lines of code to render. Defaults to 3.219        theme (str, optional): Override pygments theme used in traceback.220        word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.221        show_locals (bool, optional): Enable display of local variables. Defaults to False.222        indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.223        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.224            Defaults to 10.225        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.226        locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.227        locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.228        suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.229        max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.230 231    """232 233    LEXERS = {234        "": "text",235        ".py": "python",236        ".pxd": "cython",237        ".pyx": "cython",238        ".pxi": "pyrex",239    }240 241    def __init__(242        self,243        trace: Optional[Trace] = None,244        *,245        width: Optional[int] = 100,246        extra_lines: int = 3,247        theme: Optional[str] = None,248        word_wrap: bool = False,249        show_locals: bool = False,250        locals_max_length: int = LOCALS_MAX_LENGTH,251        locals_max_string: int = LOCALS_MAX_STRING,252        locals_hide_dunder: bool = True,253        locals_hide_sunder: bool = False,254        indent_guides: bool = True,255        suppress: Iterable[Union[str, ModuleType]] = (),256        max_frames: int = 100,257    ):258        if trace is None:259            exc_type, exc_value, traceback = sys.exc_info()260            if exc_type is None or exc_value is None or traceback is None:261                raise ValueError(262                    "Value for 'trace' required if not called in except: block"263                )264            trace = self.extract(265                exc_type, exc_value, traceback, show_locals=show_locals266            )267        self.trace = trace268        self.width = width269        self.extra_lines = extra_lines270        self.theme = Syntax.get_theme(theme or "ansi_dark")271        self.word_wrap = word_wrap272        self.show_locals = show_locals273        self.indent_guides = indent_guides274        self.locals_max_length = locals_max_length275        self.locals_max_string = locals_max_string276        self.locals_hide_dunder = locals_hide_dunder277        self.locals_hide_sunder = locals_hide_sunder278 279        self.suppress: Sequence[str] = []280        for suppress_entity in suppress:281            if not isinstance(suppress_entity, str):282                assert (283                    suppress_entity.__file__ is not None284                ), f"{suppress_entity!r} must be a module with '__file__' attribute"285                path = os.path.dirname(suppress_entity.__file__)286            else:287                path = suppress_entity288            path = os.path.normpath(os.path.abspath(path))289            self.suppress.append(path)290        self.max_frames = max(4, max_frames) if max_frames > 0 else 0291 292    @classmethod293    def from_exception(294        cls,295        exc_type: Type[Any],296        exc_value: BaseException,297        traceback: Optional[TracebackType],298        *,299        width: Optional[int] = 100,300        extra_lines: int = 3,301        theme: Optional[str] = None,302        word_wrap: bool = False,303        show_locals: bool = False,304        locals_max_length: int = LOCALS_MAX_LENGTH,305        locals_max_string: int = LOCALS_MAX_STRING,306        locals_hide_dunder: bool = True,307        locals_hide_sunder: bool = False,308        indent_guides: bool = True,309        suppress: Iterable[Union[str, ModuleType]] = (),310        max_frames: int = 100,311    ) -> "Traceback":312        """Create a traceback from exception info313 314        Args:315            exc_type (Type[BaseException]): Exception type.316            exc_value (BaseException): Exception value.317            traceback (TracebackType): Python Traceback object.318            width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.319            extra_lines (int, optional): Additional lines of code to render. Defaults to 3.320            theme (str, optional): Override pygments theme used in traceback.321            word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.322            show_locals (bool, optional): Enable display of local variables. Defaults to False.323            indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.324            locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.325                Defaults to 10.326            locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.327            locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.328            locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.329            suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.330            max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.331 332        Returns:333            Traceback: A Traceback instance that may be printed.334        """335        rich_traceback = cls.extract(336            exc_type,337            exc_value,338            traceback,339            show_locals=show_locals,340            locals_max_length=locals_max_length,341            locals_max_string=locals_max_string,342            locals_hide_dunder=locals_hide_dunder,343            locals_hide_sunder=locals_hide_sunder,344        )345 346        return cls(347            rich_traceback,348            width=width,349            extra_lines=extra_lines,350            theme=theme,351            word_wrap=word_wrap,352            show_locals=show_locals,353            indent_guides=indent_guides,354            locals_max_length=locals_max_length,355            locals_max_string=locals_max_string,356            locals_hide_dunder=locals_hide_dunder,357            locals_hide_sunder=locals_hide_sunder,358            suppress=suppress,359            max_frames=max_frames,360        )361 362    @classmethod363    def extract(364        cls,365        exc_type: Type[BaseException],366        exc_value: BaseException,367        traceback: Optional[TracebackType],368        *,369        show_locals: bool = False,370        locals_max_length: int = LOCALS_MAX_LENGTH,371        locals_max_string: int = LOCALS_MAX_STRING,372        locals_hide_dunder: bool = True,373        locals_hide_sunder: bool = False,374    ) -> Trace:375        """Extract traceback information.376 377        Args:378            exc_type (Type[BaseException]): Exception type.379            exc_value (BaseException): Exception value.380            traceback (TracebackType): Python Traceback object.381            show_locals (bool, optional): Enable display of local variables. Defaults to False.382            locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.383                Defaults to 10.384            locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.385            locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.386            locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.387 388        Returns:389            Trace: A Trace instance which you can use to construct a `Traceback`.390        """391 392        stacks: List[Stack] = []393        is_cause = False394 395        from rich import _IMPORT_CWD396 397        def safe_str(_object: Any) -> str:398            """Don't allow exceptions from __str__ to propagate."""399            try:400                return str(_object)401            except Exception:402                return "<exception str() failed>"403 404        while True:405            stack = Stack(406                exc_type=safe_str(exc_type.__name__),407                exc_value=safe_str(exc_value),408                is_cause=is_cause,409            )410 411            if isinstance(exc_value, SyntaxError):412                stack.syntax_error = _SyntaxError(413                    offset=exc_value.offset or 0,414                    filename=exc_value.filename or "?",415                    lineno=exc_value.lineno or 0,416                    line=exc_value.text or "",417                    msg=exc_value.msg,418                )419 420            stacks.append(stack)421            append = stack.frames.append422 423            def get_locals(424                iter_locals: Iterable[Tuple[str, object]]425            ) -> Iterable[Tuple[str, object]]:426                """Extract locals from an iterator of key pairs."""427                if not (locals_hide_dunder or locals_hide_sunder):428                    yield from iter_locals429                    return430                for key, value in iter_locals:431                    if locals_hide_dunder and key.startswith("__"):432                        continue433                    if locals_hide_sunder and key.startswith("_"):434                        continue435                    yield key, value436 437            for frame_summary, line_no in walk_tb(traceback):438                filename = frame_summary.f_code.co_filename439                if filename and not filename.startswith("<"):440                    if not os.path.isabs(filename):441                        filename = os.path.join(_IMPORT_CWD, filename)442                if frame_summary.f_locals.get("_rich_traceback_omit", False):443                    continue444 445                frame = Frame(446                    filename=filename or "?",447                    lineno=line_no,448                    name=frame_summary.f_code.co_name,449                    locals={450                        key: pretty.traverse(451                            value,452                            max_length=locals_max_length,453                            max_string=locals_max_string,454                        )455                        for key, value in get_locals(frame_summary.f_locals.items())456                    }457                    if show_locals458                    else None,459                )460                append(frame)461                if frame_summary.f_locals.get("_rich_traceback_guard", False):462                    del stack.frames[:]463 464            cause = getattr(exc_value, "__cause__", None)465            if cause:466                exc_type = cause.__class__467                exc_value = cause468                # __traceback__ can be None, e.g. for exceptions raised by the469                # 'multiprocessing' module470                traceback = cause.__traceback__471                is_cause = True472                continue473 474            cause = exc_value.__context__475            if cause and not getattr(exc_value, "__suppress_context__", False):476                exc_type = cause.__class__477                exc_value = cause478                traceback = cause.__traceback__479                is_cause = False480                continue481            # No cover, code is reached but coverage doesn't recognize it.482            break  # pragma: no cover483 484        trace = Trace(stacks=stacks)485        return trace486 487    def __rich_console__(488        self, console: Console, options: ConsoleOptions489    ) -> RenderResult:490        theme = self.theme491        background_style = theme.get_background_style()492        token_style = theme.get_style_for_token493 494        traceback_theme = Theme(495            {496                "pretty": token_style(TextToken),497                "pygments.text": token_style(Token),498                "pygments.string": token_style(String),499                "pygments.function": token_style(Name.Function),500                "pygments.number": token_style(Number),501                "repr.indent": token_style(Comment) + Style(dim=True),502                "repr.str": token_style(String),503                "repr.brace": token_style(TextToken) + Style(bold=True),504                "repr.number": token_style(Number),505                "repr.bool_true": token_style(Keyword.Constant),506                "repr.bool_false": token_style(Keyword.Constant),507                "repr.none": token_style(Keyword.Constant),508                "scope.border": token_style(String.Delimiter),509                "scope.equals": token_style(Operator),510                "scope.key": token_style(Name),511                "scope.key.special": token_style(Name.Constant) + Style(dim=True),512            },513            inherit=False,514        )515 516        highlighter = ReprHighlighter()517        for last, stack in loop_last(reversed(self.trace.stacks)):518            if stack.frames:519                stack_renderable: ConsoleRenderable = Panel(520                    self._render_stack(stack),521                    title="[traceback.title]Traceback [dim](most recent call last)",522                    style=background_style,523                    border_style="traceback.border",524                    expand=True,525                    padding=(0, 1),526                )527                stack_renderable = Constrain(stack_renderable, self.width)528                with console.use_theme(traceback_theme):529                    yield stack_renderable530            if stack.syntax_error is not None:531                with console.use_theme(traceback_theme):532                    yield Constrain(533                        Panel(534                            self._render_syntax_error(stack.syntax_error),535                            style=background_style,536                            border_style="traceback.border.syntax_error",537                            expand=True,538                            padding=(0, 1),539                            width=self.width,540                        ),541                        self.width,542                    )543                yield Text.assemble(544                    (f"{stack.exc_type}: ", "traceback.exc_type"),545                    highlighter(stack.syntax_error.msg),546                )547            elif stack.exc_value:548                yield Text.assemble(549                    (f"{stack.exc_type}: ", "traceback.exc_type"),550                    highlighter(stack.exc_value),551                )552            else:553                yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type"))554 555            if not last:556                if stack.is_cause:557                    yield Text.from_markup(558                        "\n[i]The above exception was the direct cause of the following exception:\n",559                    )560                else:561                    yield Text.from_markup(562                        "\n[i]During handling of the above exception, another exception occurred:\n",563                    )564 565    @group()566    def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:567        highlighter = ReprHighlighter()568        path_highlighter = PathHighlighter()569        if syntax_error.filename != "<stdin>":570            if os.path.exists(syntax_error.filename):571                text = Text.assemble(572                    (f" {syntax_error.filename}", "pygments.string"),573                    (":", "pygments.text"),574                    (str(syntax_error.lineno), "pygments.number"),575                    style="pygments.text",576                )577                yield path_highlighter(text)578        syntax_error_text = highlighter(syntax_error.line.rstrip())579        syntax_error_text.no_wrap = True580        offset = min(syntax_error.offset - 1, len(syntax_error_text))581        syntax_error_text.stylize("bold underline", offset, offset)582        syntax_error_text += Text.from_markup(583            "\n" + " " * offset + "[traceback.offset]▲[/]",584            style="pygments.text",585        )586        yield syntax_error_text587 588    @classmethod589    def _guess_lexer(cls, filename: str, code: str) -> str:590        ext = os.path.splitext(filename)[-1]591        if not ext:592            # No extension, look at first line to see if it is a hashbang593            # Note, this is an educated guess and not a guarantee594            # If it fails, the only downside is that the code is highlighted strangely595            new_line_index = code.index("\n")596            first_line = code[:new_line_index] if new_line_index != -1 else code597            if first_line.startswith("#!") and "python" in first_line.lower():598                return "python"599        try:600            return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name601        except ClassNotFound:602            return "text"603 604    @group()605    def _render_stack(self, stack: Stack) -> RenderResult:606        path_highlighter = PathHighlighter()607        theme = self.theme608 609        def read_code(filename: str) -> str:610            """Read files, and cache results on filename.611 612            Args:613                filename (str): Filename to read614 615            Returns:616                str: Contents of file617            """618            return "".join(linecache.getlines(filename))619 620        def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:621            if frame.locals:622                yield render_scope(623                    frame.locals,624                    title="locals",625                    indent_guides=self.indent_guides,626                    max_length=self.locals_max_length,627                    max_string=self.locals_max_string,628                )629 630        exclude_frames: Optional[range] = None631        if self.max_frames != 0:632            exclude_frames = range(633                self.max_frames // 2,634                len(stack.frames) - self.max_frames // 2,635            )636 637        excluded = False638        for frame_index, frame in enumerate(stack.frames):639            if exclude_frames and frame_index in exclude_frames:640                excluded = True641                continue642 643            if excluded:644                assert exclude_frames is not None645                yield Text(646                    f"\n... {len(exclude_frames)} frames hidden ...",647                    justify="center",648                    style="traceback.error",649                )650                excluded = False651 652            first = frame_index == 0653            frame_filename = frame.filename654            suppressed = any(frame_filename.startswith(path) for path in self.suppress)655 656            if os.path.exists(frame.filename):657                text = Text.assemble(658                    path_highlighter(Text(frame.filename, style="pygments.string")),659                    (":", "pygments.text"),660                    (str(frame.lineno), "pygments.number"),661                    " in ",662                    (frame.name, "pygments.function"),663                    style="pygments.text",664                )665            else:666                text = Text.assemble(667                    "in ",668                    (frame.name, "pygments.function"),669                    (":", "pygments.text"),670                    (str(frame.lineno), "pygments.number"),671                    style="pygments.text",672                )673            if not frame.filename.startswith("<") and not first:674                yield ""675            yield text676            if frame.filename.startswith("<"):677                yield from render_locals(frame)678                continue679            if not suppressed:680                try:681                    code = read_code(frame.filename)682                    if not code:683                        # code may be an empty string if the file doesn't exist, OR684                        # if the traceback filename is generated dynamically685                        continue686                    lexer_name = self._guess_lexer(frame.filename, code)687                    syntax = Syntax(688                        code,689                        lexer_name,690                        theme=theme,691                        line_numbers=True,692                        line_range=(693                            frame.lineno - self.extra_lines,694                            frame.lineno + self.extra_lines,695                        ),696                        highlight_lines={frame.lineno},697                        word_wrap=self.word_wrap,698                        code_width=88,699                        indent_guides=self.indent_guides,700                        dedent=False,701                    )702                    yield ""703                except Exception as error:704                    yield Text.assemble(705                        (f"\n{error}", "traceback.error"),706                    )707                else:708                    yield (709                        Columns(710                            [711                                syntax,712                                *render_locals(frame),713                            ],714                            padding=1,715                        )716                        if frame.locals717                        else syntax718                    )719 720 721if __name__ == "__main__":  # pragma: no cover722    from .console import Console723 724    console = Console()725    import sys726 727    def bar(a: Any) -> None:  # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑728        one = 1729        print(one / a)730 731    def foo(a: Any) -> None:732        _rich_traceback_guard = True733        zed = {734            "characters": {735                "Paul Atreides",736                "Vladimir Harkonnen",737                "Thufir Hawat",738                "Duncan Idaho",739            },740            "atomic_types": (None, False, True),741        }742        bar(a)743 744    def error() -> None:745        try:746            try:747                foo(0)748            except:749                slfkjsldkfj  # type: ignore[name-defined]750        except:751            console.print_exception(show_locals=True)752 753    error()754 
codekingpro/portable-devtools · Team Ai