Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
traceback.py1746 linesDownload Raw Back to Lib
1"""Extract, format and print information about Python stack traces."""2 3import collections.abc4import itertools5import linecache6import sys7import textwrap8import warnings9import codeop10import keyword11import tokenize12import io13import _colorize14 15from contextlib import suppress16 17__all__ = ['extract_stack', 'extract_tb', 'format_exception',18           'format_exception_only', 'format_list', 'format_stack',19           'format_tb', 'print_exc', 'format_exc', 'print_exception',20           'print_last', 'print_stack', 'print_tb', 'clear_frames',21           'FrameSummary', 'StackSummary', 'TracebackException',22           'walk_stack', 'walk_tb', 'print_list']23 24#25# Formatting and printing lists of traceback lines.26#27 28 29def print_list(extracted_list, file=None):30    """Print the list of tuples as returned by extract_tb() or31    extract_stack() as a formatted stack trace to the given file."""32    if file is None:33        file = sys.stderr34    for item in StackSummary.from_list(extracted_list).format():35        print(item, file=file, end="")36 37def format_list(extracted_list):38    """Format a list of tuples or FrameSummary objects for printing.39 40    Given a list of tuples or FrameSummary objects as returned by41    extract_tb() or extract_stack(), return a list of strings ready42    for printing.43 44    Each string in the resulting list corresponds to the item with the45    same index in the argument list.  Each string ends in a newline;46    the strings may contain internal newlines as well, for those items47    whose source text line is not None.48    """49    return StackSummary.from_list(extracted_list).format()50 51#52# Printing and Extracting Tracebacks.53#54 55def print_tb(tb, limit=None, file=None):56    """Print up to 'limit' stack trace entries from the traceback 'tb'.57 58    If 'limit' is omitted or None, all entries are printed.  If 'file'59    is omitted or None, the output goes to sys.stderr; otherwise60    'file' should be an open file or file-like object with a write()61    method.62    """63    print_list(extract_tb(tb, limit=limit), file=file)64 65def format_tb(tb, limit=None):66    """A shorthand for 'format_list(extract_tb(tb, limit))'."""67    return extract_tb(tb, limit=limit).format()68 69def extract_tb(tb, limit=None):70    """71    Return a StackSummary object representing a list of72    pre-processed entries from traceback.73 74    This is useful for alternate formatting of stack traces.  If75    'limit' is omitted or None, all entries are extracted.  A76    pre-processed stack trace entry is a FrameSummary object77    containing attributes filename, lineno, name, and line78    representing the information that is usually printed for a stack79    trace.  The line is a string with leading and trailing80    whitespace stripped; if the source is not available it is None.81    """82    return StackSummary._extract_from_extended_frame_gen(83        _walk_tb_with_full_positions(tb), limit=limit)84 85#86# Exception formatting and output.87#88 89_cause_message = (90    "\nThe above exception was the direct cause "91    "of the following exception:\n\n")92 93_context_message = (94    "\nDuring handling of the above exception, "95    "another exception occurred:\n\n")96 97 98class _Sentinel:99    def __repr__(self):100        return "<implicit>"101 102_sentinel = _Sentinel()103 104def _parse_value_tb(exc, value, tb):105    if (value is _sentinel) != (tb is _sentinel):106        raise ValueError("Both or neither of value and tb must be given")107    if value is tb is _sentinel:108        if exc is not None:109            if isinstance(exc, BaseException):110                return exc, exc.__traceback__111 112            raise TypeError(f'Exception expected for value, '113                            f'{type(exc).__name__} found')114        else:115            return None, None116    return value, tb117 118 119def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \120                    file=None, chain=True, **kwargs):121    """Print exception up to 'limit' stack trace entries from 'tb' to 'file'.122 123    This differs from print_tb() in the following ways: (1) if124    traceback is not None, it prints a header "Traceback (most recent125    call last):"; (2) it prints the exception type and value after the126    stack trace; (3) if type is SyntaxError and value has the127    appropriate format, it prints the line where the syntax error128    occurred with a caret on the next line indicating the approximate129    position of the error.130    """131    colorize = kwargs.get("colorize", False)132    value, tb = _parse_value_tb(exc, value, tb)133    te = TracebackException(type(value), value, tb, limit=limit, compact=True)134    te.print(file=file, chain=chain, colorize=colorize)135 136 137BUILTIN_EXCEPTION_LIMIT = object()138 139 140def _print_exception_bltin(exc, /):141    file = sys.stderr if sys.stderr is not None else sys.__stderr__142    colorize = _colorize.can_colorize(file=file)143    return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)144 145 146def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \147                     chain=True, **kwargs):148    """Format a stack trace and the exception information.149 150    The arguments have the same meaning as the corresponding arguments151    to print_exception().  The return value is a list of strings, each152    ending in a newline and some containing internal newlines.  When153    these lines are concatenated and printed, exactly the same text is154    printed as does print_exception().155    """156    colorize = kwargs.get("colorize", False)157    value, tb = _parse_value_tb(exc, value, tb)158    te = TracebackException(type(value), value, tb, limit=limit, compact=True)159    return list(te.format(chain=chain, colorize=colorize))160 161 162def format_exception_only(exc, /, value=_sentinel, *, show_group=False, **kwargs):163    """Format the exception part of a traceback.164 165    The return value is a list of strings, each ending in a newline.166 167    The list contains the exception's message, which is168    normally a single string; however, for :exc:`SyntaxError` exceptions, it169    contains several lines that (when printed) display detailed information170    about where the syntax error occurred. Following the message, the list171    contains the exception's ``__notes__``.172 173    When *show_group* is ``True``, and the exception is an instance of174    :exc:`BaseExceptionGroup`, the nested exceptions are included as175    well, recursively, with indentation relative to their nesting depth.176    """177    colorize = kwargs.get("colorize", False)178    if value is _sentinel:179        value = exc180    te = TracebackException(type(value), value, None, compact=True)181    return list(te.format_exception_only(show_group=show_group, colorize=colorize))182 183 184# -- not official API but folk probably use these two functions.185 186def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=False):187    valuestr = _safe_string(value, 'exception')188    end_char = "\n" if insert_final_newline else ""189    if colorize:190        theme = _colorize.get_theme(force_color=True).traceback191    else:192        theme = _colorize.get_theme(force_no_color=True).traceback193    if value is None or not valuestr:194        line = f"{theme.type}{etype}{theme.reset}{end_char}"195    else:196        line = f"{theme.type}{etype}{theme.reset}: {theme.message}{valuestr}{theme.reset}{end_char}"197    return line198 199 200def _safe_string(value, what, func=str):201    try:202        return func(value)203    except:204        return f'<{what} {func.__name__}() failed>'205 206# --207 208def print_exc(limit=None, file=None, chain=True):209    """Shorthand for 'print_exception(sys.exception(), limit=limit, file=file, chain=chain)'."""210    print_exception(sys.exception(), limit=limit, file=file, chain=chain)211 212def format_exc(limit=None, chain=True):213    """Like print_exc() but return a string."""214    return "".join(format_exception(sys.exception(), limit=limit, chain=chain))215 216def print_last(limit=None, file=None, chain=True):217    """This is a shorthand for 'print_exception(sys.last_exc, limit=limit, file=file, chain=chain)'."""218    if not hasattr(sys, "last_exc") and not hasattr(sys, "last_type"):219        raise ValueError("no last exception")220 221    if hasattr(sys, "last_exc"):222        print_exception(sys.last_exc, limit=limit, file=file, chain=chain)223    else:224        print_exception(sys.last_type, sys.last_value, sys.last_traceback,225                        limit=limit, file=file, chain=chain)226 227 228#229# Printing and Extracting Stacks.230#231 232def print_stack(f=None, limit=None, file=None):233    """Print a stack trace from its invocation point.234 235    The optional 'f' argument can be used to specify an alternate236    stack frame at which to start. The optional 'limit' and 'file'237    arguments have the same meaning as for print_exception().238    """239    if f is None:240        f = sys._getframe().f_back241    print_list(extract_stack(f, limit=limit), file=file)242 243 244def format_stack(f=None, limit=None):245    """Shorthand for 'format_list(extract_stack(f, limit))'."""246    if f is None:247        f = sys._getframe().f_back248    return format_list(extract_stack(f, limit=limit))249 250 251def extract_stack(f=None, limit=None):252    """Extract the raw traceback from the current stack frame.253 254    The return value has the same format as for extract_tb().  The255    optional 'f' and 'limit' arguments have the same meaning as for256    print_stack().  Each item in the list is a quadruple (filename,257    line number, function name, text), and the entries are in order258    from oldest to newest stack frame.259    """260    if f is None:261        f = sys._getframe().f_back262    stack = StackSummary.extract(walk_stack(f), limit=limit)263    stack.reverse()264    return stack265 266 267def clear_frames(tb):268    "Clear all references to local variables in the frames of a traceback."269    while tb is not None:270        try:271            tb.tb_frame.clear()272        except RuntimeError:273            # Ignore the exception raised if the frame is still executing.274            pass275        tb = tb.tb_next276 277 278class FrameSummary:279    """Information about a single frame from a traceback.280 281    - :attr:`filename` The filename for the frame.282    - :attr:`lineno` The line within filename for the frame that was283      active when the frame was captured.284    - :attr:`name` The name of the function or method that was executing285      when the frame was captured.286    - :attr:`line` The text from the linecache module for the287      of code that was running when the frame was captured.288    - :attr:`locals` Either None if locals were not supplied, or a dict289      mapping the name to the repr() of the variable.290    """291 292    __slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno',293                 'name', '_lines', '_lines_dedented', 'locals', '_code')294 295    def __init__(self, filename, lineno, name, *, lookup_line=True,296            locals=None, line=None,297            end_lineno=None, colno=None, end_colno=None, **kwargs):298        """Construct a FrameSummary.299 300        :param lookup_line: If True, `linecache` is consulted for the source301            code line. Otherwise, the line will be looked up when first needed.302        :param locals: If supplied the frame locals, which will be captured as303            object representations.304        :param line: If provided, use this instead of looking up the line in305            the linecache.306        """307        self.filename = filename308        self.lineno = lineno309        self.end_lineno = lineno if end_lineno is None else end_lineno310        self.colno = colno311        self.end_colno = end_colno312        self.name = name313        self._code = kwargs.get("_code")314        self._lines = line315        self._lines_dedented = None316        if lookup_line:317            self.line318        self.locals = {k: _safe_string(v, 'local', func=repr)319            for k, v in locals.items()} if locals else None320 321    def __eq__(self, other):322        if isinstance(other, FrameSummary):323            return (self.filename == other.filename and324                    self.lineno == other.lineno and325                    self.name == other.name and326                    self.locals == other.locals)327        if isinstance(other, tuple):328            return (self.filename, self.lineno, self.name, self.line) == other329        return NotImplemented330 331    def __getitem__(self, pos):332        return (self.filename, self.lineno, self.name, self.line)[pos]333 334    def __iter__(self):335        return iter([self.filename, self.lineno, self.name, self.line])336 337    def __repr__(self):338        return "<FrameSummary file {filename}, line {lineno} in {name}>".format(339            filename=self.filename, lineno=self.lineno, name=self.name)340 341    def __len__(self):342        return 4343 344    def _set_lines(self):345        if (346            self._lines is None347            and self.lineno is not None348            and self.end_lineno is not None349        ):350            lines = []351            for lineno in range(self.lineno, self.end_lineno + 1):352                # treat errors (empty string) and empty lines (newline) as the same353                line = linecache.getline(self.filename, lineno).rstrip()354                if not line and self._code is not None and self.filename.startswith("<"):355                    line = linecache._getline_from_code(self._code, lineno).rstrip()356                lines.append(line)357            self._lines = "\n".join(lines) + "\n"358 359    @property360    def _original_lines(self):361        # Returns the line as-is from the source, without modifying whitespace.362        self._set_lines()363        return self._lines364 365    @property366    def _dedented_lines(self):367        # Returns _original_lines, but dedented368        self._set_lines()369        if self._lines_dedented is None and self._lines is not None:370            self._lines_dedented = textwrap.dedent(self._lines)371        return self._lines_dedented372 373    @property374    def line(self):375        self._set_lines()376        if self._lines is None:377            return None378        # return only the first line, stripped379        return self._lines.partition("\n")[0].strip()380 381 382def walk_stack(f):383    """Walk a stack yielding the frame and line number for each frame.384 385    This will follow f.f_back from the given frame. If no frame is given, the386    current stack is used. Usually used with StackSummary.extract.387    """388    if f is None:389        f = sys._getframe().f_back390 391    def walk_stack_generator(frame):392        while frame is not None:393            yield frame, frame.f_lineno394            frame = frame.f_back395 396    return walk_stack_generator(f)397 398 399def walk_tb(tb):400    """Walk a traceback yielding the frame and line number for each frame.401 402    This will follow tb.tb_next (and thus is in the opposite order to403    walk_stack). Usually used with StackSummary.extract.404    """405    while tb is not None:406        yield tb.tb_frame, tb.tb_lineno407        tb = tb.tb_next408 409 410def _walk_tb_with_full_positions(tb):411    # Internal version of walk_tb that yields full code positions including412    # end line and column information.413    while tb is not None:414        positions = _get_code_position(tb.tb_frame.f_code, tb.tb_lasti)415        # Yield tb_lineno when co_positions does not have a line number to416        # maintain behavior with walk_tb.417        if positions[0] is None:418            yield tb.tb_frame, (tb.tb_lineno, ) + positions[1:]419        else:420            yield tb.tb_frame, positions421        tb = tb.tb_next422 423 424def _get_code_position(code, instruction_index):425    if instruction_index < 0:426        return (None, None, None, None)427    positions_gen = code.co_positions()428    return next(itertools.islice(positions_gen, instruction_index // 2, None))429 430 431_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.432 433 434class StackSummary(list):435    """A list of FrameSummary objects, representing a stack of frames."""436 437    @classmethod438    def extract(klass, frame_gen, *, limit=None, lookup_lines=True,439            capture_locals=False):440        """Create a StackSummary from a traceback or stack object.441 442        :param frame_gen: A generator that yields (frame, lineno) tuples443            whose summaries are to be included in the stack.444        :param limit: None to include all frames or the number of frames to445            include.446        :param lookup_lines: If True, lookup lines for each frame immediately,447            otherwise lookup is deferred until the frame is rendered.448        :param capture_locals: If True, the local variables from each frame will449            be captured as object representations into the FrameSummary.450        """451        def extended_frame_gen():452            for f, lineno in frame_gen:453                yield f, (lineno, None, None, None)454 455        return klass._extract_from_extended_frame_gen(456            extended_frame_gen(), limit=limit, lookup_lines=lookup_lines,457            capture_locals=capture_locals)458 459    @classmethod460    def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None,461            lookup_lines=True, capture_locals=False):462        # Same as extract but operates on a frame generator that yields463        # (frame, (lineno, end_lineno, colno, end_colno)) in the stack.464        # Only lineno is required, the remaining fields can be None if the465        # information is not available.466        builtin_limit = limit is BUILTIN_EXCEPTION_LIMIT467        if limit is None or builtin_limit:468            limit = getattr(sys, 'tracebacklimit', None)469            if limit is not None and limit < 0:470                limit = 0471        if limit is not None:472            if builtin_limit:473                frame_gen = tuple(frame_gen)474                frame_gen = frame_gen[len(frame_gen) - limit:]475            elif limit >= 0:476                frame_gen = itertools.islice(frame_gen, limit)477            else:478                frame_gen = collections.deque(frame_gen, maxlen=-limit)479 480        result = klass()481        fnames = set()482        for f, (lineno, end_lineno, colno, end_colno) in frame_gen:483            co = f.f_code484            filename = co.co_filename485            name = co.co_name486            fnames.add(filename)487            linecache.lazycache(filename, f.f_globals)488            # Must defer line lookups until we have called checkcache.489            if capture_locals:490                f_locals = f.f_locals491            else:492                f_locals = None493            result.append(494                FrameSummary(filename, lineno, name,495                    lookup_line=False, locals=f_locals,496                    end_lineno=end_lineno, colno=colno, end_colno=end_colno,497                    _code=f.f_code,498                )499            )500        for filename in fnames:501            linecache.checkcache(filename)502 503        # If immediate lookup was desired, trigger lookups now.504        if lookup_lines:505            for f in result:506                f.line507        return result508 509    @classmethod510    def from_list(klass, a_list):511        """512        Create a StackSummary object from a supplied list of513        FrameSummary objects or old-style list of tuples.514        """515        # While doing a fast-path check for isinstance(a_list, StackSummary) is516        # appealing, idlelib.run.cleanup_traceback and other similar code may517        # break this by making arbitrary frames plain tuples, so we need to518        # check on a frame by frame basis.519        result = StackSummary()520        for frame in a_list:521            if isinstance(frame, FrameSummary):522                result.append(frame)523            else:524                filename, lineno, name, line = frame525                result.append(FrameSummary(filename, lineno, name, line=line))526        return result527 528    def format_frame_summary(self, frame_summary, **kwargs):529        """Format the lines for a single FrameSummary.530 531        Returns a string representing one frame involved in the stack. This532        gets called for every frame to be printed in the stack summary.533        """534        colorize = kwargs.get("colorize", False)535        row = []536        filename = frame_summary.filename537        if frame_summary.filename.startswith("<stdin-") and frame_summary.filename.endswith('>'):538            filename = "<stdin>"539        if colorize:540            theme = _colorize.get_theme(force_color=True).traceback541        else:542            theme = _colorize.get_theme(force_no_color=True).traceback543        row.append(544            '  File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format(545                theme.filename,546                filename,547                theme.reset,548                theme.line_no,549                frame_summary.lineno,550                theme.reset,551                theme.frame,552                frame_summary.name,553                theme.reset,554            )555        )556        if frame_summary._dedented_lines and frame_summary._dedented_lines.strip():557            if (558                frame_summary.colno is None or559                frame_summary.end_colno is None560            ):561                # only output first line if column information is missing562                row.append(textwrap.indent(frame_summary.line, '    ') + "\n")563            else:564                # get first and last line565                all_lines_original = frame_summary._original_lines.splitlines()566                first_line = all_lines_original[0]567                # assume all_lines_original has enough lines (since we constructed it)568                last_line = all_lines_original[frame_summary.end_lineno - frame_summary.lineno]569 570                # character index of the start/end of the instruction571                start_offset = _byte_offset_to_character_offset(first_line, frame_summary.colno)572                end_offset = _byte_offset_to_character_offset(last_line, frame_summary.end_colno)573 574                all_lines = frame_summary._dedented_lines.splitlines()[575                    :frame_summary.end_lineno - frame_summary.lineno + 1576                ]577 578                # adjust start/end offset based on dedent579                dedent_characters = len(first_line) - len(all_lines[0])580                start_offset = max(0, start_offset - dedent_characters)581                end_offset = max(0, end_offset - dedent_characters)582 583                # When showing this on a terminal, some of the non-ASCII characters584                # might be rendered as double-width characters, so we need to take585                # that into account when calculating the length of the line.586                dp_start_offset = _display_width(all_lines[0], offset=start_offset)587                dp_end_offset = _display_width(all_lines[-1], offset=end_offset)588 589                # get exact code segment corresponding to the instruction590                segment = "\n".join(all_lines)591                segment = segment[start_offset:len(segment) - (len(all_lines[-1]) - end_offset)]592 593                # attempt to parse for anchors594                anchors = None595                show_carets = False596                with suppress(Exception):597                    anchors = _extract_caret_anchors_from_line_segment(segment)598                show_carets = self._should_show_carets(start_offset, end_offset, all_lines, anchors)599 600                result = []601 602                # only display first line, last line, and lines around anchor start/end603                significant_lines = {0, len(all_lines) - 1}604 605                anchors_left_end_offset = 0606                anchors_right_start_offset = 0607                primary_char = "^"608                secondary_char = "^"609                if anchors:610                    anchors_left_end_offset = anchors.left_end_offset611                    anchors_right_start_offset = anchors.right_start_offset612                    # computed anchor positions do not take start_offset into account,613                    # so account for it here614                    if anchors.left_end_lineno == 0:615                        anchors_left_end_offset += start_offset616                    if anchors.right_start_lineno == 0:617                        anchors_right_start_offset += start_offset618 619                    # account for display width620                    anchors_left_end_offset = _display_width(621                        all_lines[anchors.left_end_lineno], offset=anchors_left_end_offset622                    )623                    anchors_right_start_offset = _display_width(624                        all_lines[anchors.right_start_lineno], offset=anchors_right_start_offset625                    )626 627                    primary_char = anchors.primary_char628                    secondary_char = anchors.secondary_char629                    significant_lines.update(630                        range(anchors.left_end_lineno - 1, anchors.left_end_lineno + 2)631                    )632                    significant_lines.update(633                        range(anchors.right_start_lineno - 1, anchors.right_start_lineno + 2)634                    )635 636                # remove bad line numbers637                significant_lines.discard(-1)638                significant_lines.discard(len(all_lines))639 640                def output_line(lineno):641                    """output all_lines[lineno] along with carets"""642                    result.append(all_lines[lineno] + "\n")643                    if not show_carets:644                        return645                    num_spaces = len(all_lines[lineno]) - len(all_lines[lineno].lstrip())646                    carets = []647                    num_carets = dp_end_offset if lineno == len(all_lines) - 1 else _display_width(all_lines[lineno])648                    # compute caret character for each position649                    for col in range(num_carets):650                        if col < num_spaces or (lineno == 0 and col < dp_start_offset):651                            # before first non-ws char of the line, or before start of instruction652                            carets.append(' ')653                        elif anchors and (654                            lineno > anchors.left_end_lineno or655                            (lineno == anchors.left_end_lineno and col >= anchors_left_end_offset)656                        ) and (657                            lineno < anchors.right_start_lineno or658                            (lineno == anchors.right_start_lineno and col < anchors_right_start_offset)659                        ):660                            # within anchors661                            carets.append(secondary_char)662                        else:663                            carets.append(primary_char)664                    if colorize:665                        # Replace the previous line with a red version of it only in the parts covered666                        # by the carets.667                        line = result[-1]668                        colorized_line_parts = []669                        colorized_carets_parts = []670 671                        for color, group in itertools.groupby(itertools.zip_longest(line, carets, fillvalue=""), key=lambda x: x[1]):672                            caret_group = list(group)673                            if color == "^":674                                colorized_line_parts.append(theme.error_highlight + "".join(char for char, _ in caret_group) + theme.reset)675                                colorized_carets_parts.append(theme.error_highlight + "".join(caret for _, caret in caret_group) + theme.reset)676                            elif color == "~":677                                colorized_line_parts.append(theme.error_range + "".join(char for char, _ in caret_group) + theme.reset)678                                colorized_carets_parts.append(theme.error_range + "".join(caret for _, caret in caret_group) + theme.reset)679                            else:680                                colorized_line_parts.append("".join(char for char, _ in caret_group))681                                colorized_carets_parts.append("".join(caret for _, caret in caret_group))682 683                        colorized_line = "".join(colorized_line_parts)684                        colorized_carets = "".join(colorized_carets_parts)685                        result[-1] = colorized_line686                        result.append(colorized_carets + "\n")687                    else:688                        result.append("".join(carets) + "\n")689 690                # display significant lines691                sig_lines_list = sorted(significant_lines)692                for i, lineno in enumerate(sig_lines_list):693                    if i:694                        linediff = lineno - sig_lines_list[i - 1]695                        if linediff == 2:696                            # 1 line in between - just output it697                            output_line(lineno - 1)698                        elif linediff > 2:699                            # > 1 line in between - abbreviate700                            result.append(f"...<{linediff - 1} lines>...\n")701                    output_line(lineno)702 703                row.append(704                    textwrap.indent(textwrap.dedent("".join(result)), '    ', lambda line: True)705                )706        if frame_summary.locals:707            for name, value in sorted(frame_summary.locals.items()):708                row.append('    {name} = {value}\n'.format(name=name, value=value))709 710        return ''.join(row)711 712    def _should_show_carets(self, start_offset, end_offset, all_lines, anchors):713        with suppress(SyntaxError, ImportError):714            import ast715            tree = ast.parse('\n'.join(all_lines))716            if not tree.body:717                return False718            statement = tree.body[0]719            value = None720            def _spawns_full_line(value):721                return (722                    value.lineno == 1723                    and value.end_lineno == len(all_lines)724                    and value.col_offset == start_offset725                    and value.end_col_offset == end_offset726                )727            match statement:728                case ast.Return(value=ast.Call()):729                    if isinstance(statement.value.func, ast.Name):730                        value = statement.value731                case ast.Assign(value=ast.Call()):732                    if (733                        len(statement.targets) == 1 and734                        isinstance(statement.targets[0], ast.Name)735                    ):736                        value = statement.value737            if value is not None and _spawns_full_line(value):738                return False739        if anchors:740            return True741        if all_lines[0][:start_offset].lstrip() or all_lines[-1][end_offset:].rstrip():742            return True743        return False744 745    def format(self, **kwargs):746        """Format the stack ready for printing.747 748        Returns a list of strings ready for printing.  Each string in the749        resulting list corresponds to a single frame from the stack.750        Each string ends in a newline; the strings may contain internal751        newlines as well, for those items with source text lines.752 753        For long sequences of the same frame and line, the first few754        repetitions are shown, followed by a summary line stating the exact755        number of further repetitions.756        """757        colorize = kwargs.get("colorize", False)758        result = []759        last_file = None760        last_line = None761        last_name = None762        count = 0763        for frame_summary in self:764            formatted_frame = self.format_frame_summary(frame_summary, colorize=colorize)765            if formatted_frame is None:766                continue767            if (last_file is None or last_file != frame_summary.filename or768                last_line is None or last_line != frame_summary.lineno or769                last_name is None or last_name != frame_summary.name):770                if count > _RECURSIVE_CUTOFF:771                    count -= _RECURSIVE_CUTOFF772                    result.append(773                        f'  [Previous line repeated {count} more '774                        f'time{"s" if count > 1 else ""}]\n'775                    )776                last_file = frame_summary.filename777                last_line = frame_summary.lineno778                last_name = frame_summary.name779                count = 0780            count += 1781            if count > _RECURSIVE_CUTOFF:782                continue783            result.append(formatted_frame)784 785        if count > _RECURSIVE_CUTOFF:786            count -= _RECURSIVE_CUTOFF787            result.append(788                f'  [Previous line repeated {count} more '789                f'time{"s" if count > 1 else ""}]\n'790            )791        return result792 793 794def _byte_offset_to_character_offset(str, offset):795    as_utf8 = str.encode('utf-8')796    return len(as_utf8[:offset].decode("utf-8", errors="replace"))797 798 799_Anchors = collections.namedtuple(800    "_Anchors",801    [802        "left_end_lineno",803        "left_end_offset",804        "right_start_lineno",805        "right_start_offset",806        "primary_char",807        "secondary_char",808    ],809    defaults=["~", "^"]810)811 812def _extract_caret_anchors_from_line_segment(segment):813    """814    Given source code `segment` corresponding to a FrameSummary, determine:815        - for binary ops, the location of the binary op816        - for indexing and function calls, the location of the brackets.817    `segment` is expected to be a valid Python expression.818    """819    import ast820 821    try:822        # Without parentheses, `segment` is parsed as a statement.823        # Binary ops, subscripts, and calls are expressions, so824        # we can wrap them with parentheses to parse them as825        # (possibly multi-line) expressions.826        # e.g. if we try to highlight the addition in827        # x = (828        #     a +829        #     b830        # )831        # then we would ast.parse832        #     a +833        #     b834        # which is not a valid statement because of the newline.835        # Adding brackets makes it a valid expression.836        # (837        #     a +838        #     b839        # )840        # Line locations will be different than the original,841        # which is taken into account later on.842        tree = ast.parse(f"(\n{segment}\n)")843    except SyntaxError:844        return None845 846    if len(tree.body) != 1:847        return None848 849    lines = segment.splitlines()850 851    def normalize(lineno, offset):852        """Get character index given byte offset"""853        return _byte_offset_to_character_offset(lines[lineno], offset)854 855    def next_valid_char(lineno, col):856        """Gets the next valid character index in `lines`, if857        the current location is not valid. Handles empty lines.858        """859        while lineno < len(lines) and col >= len(lines[lineno]):860            col = 0861            lineno += 1862        assert lineno < len(lines) and col < len(lines[lineno])863        return lineno, col864 865    def increment(lineno, col):866        """Get the next valid character index in `lines`."""867        col += 1868        lineno, col = next_valid_char(lineno, col)869        return lineno, col870 871    def nextline(lineno, col):872        """Get the next valid character at least on the next line"""873        col = 0874        lineno += 1875        lineno, col = next_valid_char(lineno, col)876        return lineno, col877 878    def increment_until(lineno, col, stop):879        """Get the next valid non-"\\#" character that satisfies the `stop` predicate"""880        while True:881            ch = lines[lineno][col]882            if ch in "\\#":883                lineno, col = nextline(lineno, col)884            elif not stop(ch):885                lineno, col = increment(lineno, col)886            else:887                break888        return lineno, col889 890    def setup_positions(expr, force_valid=True):891        """Get the lineno/col position of the end of `expr`. If `force_valid` is True,892        forces the position to be a valid character (e.g. if the position is beyond the893        end of the line, move to the next line)894        """895        # -2 since end_lineno is 1-indexed and because we added an extra896        # bracket + newline to `segment` when calling ast.parse897        lineno = expr.end_lineno - 2898        col = normalize(lineno, expr.end_col_offset)899        return next_valid_char(lineno, col) if force_valid else (lineno, col)900 901    statement = tree.body[0]902    match statement:903        case ast.Expr(expr):904            match expr:905                case ast.BinOp():906                    # ast gives these locations for BinOp subexpressions907                    # ( left_expr ) + ( right_expr )908                    #   left^^^^^       right^^^^^909                    lineno, col = setup_positions(expr.left)910 911                    # First operator character is the first non-space/')' character912                    lineno, col = increment_until(lineno, col, lambda x: not x.isspace() and x != ')')913 914                    # binary op is 1 or 2 characters long, on the same line,915                    # before the right subexpression916                    right_col = col + 1917                    if (918                        right_col < len(lines[lineno])919                        and (920                            # operator char should not be in the right subexpression921                            expr.right.lineno - 2 > lineno or922                            right_col < normalize(expr.right.lineno - 2, expr.right.col_offset)923                        )924                        and not (ch := lines[lineno][right_col]).isspace()925                        and ch not in "\\#"926                    ):927                        right_col += 1928 929                    # right_col can be invalid since it is exclusive930                    return _Anchors(lineno, col, lineno, right_col)931                case ast.Subscript():932                    # ast gives these locations for value and slice subexpressions933                    # ( value_expr ) [ slice_expr ]934                    #   value^^^^^     slice^^^^^935                    # subscript^^^^^^^^^^^^^^^^^^^^936 937                    # find left bracket938                    left_lineno, left_col = setup_positions(expr.value)939                    left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '[')940                    # find right bracket (final character of expression)941                    right_lineno, right_col = setup_positions(expr, force_valid=False)942                    return _Anchors(left_lineno, left_col, right_lineno, right_col)943                case ast.Call():944                    # ast gives these locations for function call expressions945                    # ( func_expr ) (args, kwargs)946                    #   func^^^^^947                    # call^^^^^^^^^^^^^^^^^^^^^^^^948 949                    # find left bracket950                    left_lineno, left_col = setup_positions(expr.func)951                    left_lineno, left_col = increment_until(left_lineno, left_col, lambda x: x == '(')952                    # find right bracket (final character of expression)953                    right_lineno, right_col = setup_positions(expr, force_valid=False)954                    return _Anchors(left_lineno, left_col, right_lineno, right_col)955 956    return None957 958_WIDE_CHAR_SPECIFIERS = "WF"959 960def _display_width(line, offset=None):961    """Calculate the extra amount of width space the given source962    code segment might take if it were to be displayed on a fixed963    width output device. Supports wide unicode characters and emojis."""964 965    if offset is None:966        offset = len(line)967 968    # Fast track for ASCII-only strings969    if line.isascii():970        return offset971 972    import unicodedata973 974    return sum(975        2 if unicodedata.east_asian_width(char) in _WIDE_CHAR_SPECIFIERS else 1976        for char in line[:offset]977    )978 979 980 981class _ExceptionPrintContext:982    def __init__(self):983        self.seen = set()984        self.exception_group_depth = 0985        self.need_close = False986 987    def indent(self):988        return ' ' * (2 * self.exception_group_depth)989 990    def emit(self, text_gen, margin_char=None):991        if margin_char is None:992            margin_char = '|'993        indent_str = self.indent()994        if self.exception_group_depth:995            indent_str += margin_char + ' '996 997        if isinstance(text_gen, str):998            yield textwrap.indent(text_gen, indent_str, lambda line: True)999        else:1000            for text in text_gen:1001                yield textwrap.indent(text, indent_str, lambda line: True)1002 1003 1004class TracebackException:1005    """An exception ready for rendering.1006 1007    The traceback module captures enough attributes from the original exception1008    to this intermediary form to ensure that no references are held, while1009    still being able to fully print or format it.1010 1011    max_group_width and max_group_depth control the formatting of exception1012    groups. The depth refers to the nesting level of the group, and the width1013    refers to the size of a single exception group's exceptions array. The1014    formatted output is truncated when either limit is exceeded.1015 1016    Use `from_exception` to create TracebackException instances from exception1017    objects, or the constructor to create TracebackException instances from1018    individual components.1019 1020    - :attr:`__cause__` A TracebackException of the original *__cause__*.1021    - :attr:`__context__` A TracebackException of the original *__context__*.1022    - :attr:`exceptions` For exception groups - a list of TracebackException1023      instances for the nested *exceptions*.  ``None`` for other exceptions.1024    - :attr:`__suppress_context__` The *__suppress_context__* value from the1025      original exception.1026    - :attr:`stack` A `StackSummary` representing the traceback.1027    - :attr:`exc_type` (deprecated) The class of the original traceback.1028    - :attr:`exc_type_str` String display of exc_type1029    - :attr:`filename` For syntax errors - the filename where the error1030      occurred.1031    - :attr:`lineno` For syntax errors - the linenumber where the error1032      occurred.1033    - :attr:`end_lineno` For syntax errors - the end linenumber where the error1034      occurred. Can be `None` if not present.1035    - :attr:`text` For syntax errors - the text where the error1036      occurred.1037    - :attr:`offset` For syntax errors - the offset into the text where the1038      error occurred.1039    - :attr:`end_offset` For syntax errors - the end offset into the text where1040      the error occurred. Can be `None` if not present.1041    - :attr:`msg` For syntax errors - the compiler error message.1042    """1043 1044    def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,1045            lookup_lines=True, capture_locals=False, compact=False,1046            max_group_width=15, max_group_depth=10, save_exc_type=True, _seen=None):1047        # NB: we need to accept exc_traceback, exc_value, exc_traceback to1048        # permit backwards compat with the existing API, otherwise we1049        # need stub thunk objects just to glue it together.1050        # Handle loops in __cause__ or __context__.1051        is_recursive_call = _seen is not None1052        if _seen is None:1053            _seen = set()1054        _seen.add(id(exc_value))1055 1056        self.max_group_width = max_group_width1057        self.max_group_depth = max_group_depth1058 1059        self.stack = StackSummary._extract_from_extended_frame_gen(1060            _walk_tb_with_full_positions(exc_traceback),1061            limit=limit, lookup_lines=lookup_lines,1062            capture_locals=capture_locals)1063 1064        self._exc_type = exc_type if save_exc_type else None1065 1066        # Capture now to permit freeing resources: only complication is in the1067        # unofficial API _format_final_exc_line1068        self._str = _safe_string(exc_value, 'exception')1069        try:1070            self.__notes__ = getattr(exc_value, '__notes__', None)1071        except Exception as e:1072            self.__notes__ = [1073                f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}']1074 1075        self._is_syntax_error = False1076        self._have_exc_type = exc_type is not None1077        if exc_type is not None:1078            self.exc_type_qualname = exc_type.__qualname__1079            self.exc_type_module = exc_type.__module__1080        else:1081            self.exc_type_qualname = None1082            self.exc_type_module = None1083 1084        if exc_type and issubclass(exc_type, SyntaxError):1085            # Handle SyntaxError's specially1086            self.filename = exc_value.filename1087            lno = exc_value.lineno1088            self.lineno = str(lno) if lno is not None else None1089            end_lno = exc_value.end_lineno1090            self.end_lineno = str(end_lno) if end_lno is not None else None1091            self.text = exc_value.text1092            self.offset = exc_value.offset1093            self.end_offset = exc_value.end_offset1094            self.msg = exc_value.msg1095            self._is_syntax_error = True1096            self._exc_metadata = getattr(exc_value, "_metadata", None)1097        elif exc_type and issubclass(exc_type, ImportError) and \1098                getattr(exc_value, "name_from", None) is not None:1099            wrong_name = getattr(exc_value, "name_from", None)1100            suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)1101            if suggestion:1102                self._str += f". Did you mean: '{suggestion}'?"1103        elif exc_type and issubclass(exc_type, (NameError, AttributeError)) and \1104                getattr(exc_value, "name", None) is not None:1105            wrong_name = getattr(exc_value, "name", None)1106            suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)1107            if suggestion:1108                self._str += f". Did you mean: '{suggestion}'?"1109            if issubclass(exc_type, NameError):1110                wrong_name = getattr(exc_value, "name", None)1111                if wrong_name is not None and wrong_name in sys.stdlib_module_names:1112                    if suggestion:1113                        self._str += f" Or did you forget to import '{wrong_name}'?"1114                    else:1115                        self._str += f". Did you forget to import '{wrong_name}'?"1116        if lookup_lines:1117            self._load_lines()1118        self.__suppress_context__ = \1119            exc_value.__suppress_context__ if exc_value is not None else False1120 1121        # Convert __cause__ and __context__ to `TracebackExceptions`s, use a1122        # queue to avoid recursion (only the top-level call gets _seen == None)1123        if not is_recursive_call:1124            queue = [(self, exc_value)]1125            while queue:1126                te, e = queue.pop()1127                if (e is not None and e.__cause__ is not None1128                    and id(e.__cause__) not in _seen):1129                    cause = TracebackException(1130                        type(e.__cause__),1131                        e.__cause__,1132                        e.__cause__.__traceback__,1133                        limit=limit,1134                        lookup_lines=lookup_lines,1135                        capture_locals=capture_locals,1136                        max_group_width=max_group_width,1137                        max_group_depth=max_group_depth,1138                        _seen=_seen)1139                else:1140                    cause = None1141 1142                if compact:1143                    need_context = (cause is None and1144                                    e is not None and1145                                    not e.__suppress_context__)1146                else:1147                    need_context = True1148                if (e is not None and e.__context__ is not None1149                    and need_context and id(e.__context__) not in _seen):1150                    context = TracebackException(1151                        type(e.__context__),1152                        e.__context__,1153                        e.__context__.__traceback__,1154                        limit=limit,1155                        lookup_lines=lookup_lines,1156                        capture_locals=capture_locals,1157                        max_group_width=max_group_width,1158                        max_group_depth=max_group_depth,1159                        _seen=_seen)1160                else:1161                    context = None1162 1163                if e is not None and isinstance(e, BaseExceptionGroup):1164                    exceptions = []1165                    for exc in e.exceptions:1166                        texc = TracebackException(1167                            type(exc),1168                            exc,1169                            exc.__traceback__,1170                            limit=limit,1171                            lookup_lines=lookup_lines,1172                            capture_locals=capture_locals,1173                            max_group_width=max_group_width,1174                            max_group_depth=max_group_depth,1175                            _seen=_seen)1176                        exceptions.append(texc)1177                else:1178                    exceptions = None1179 1180                te.__cause__ = cause1181                te.__context__ = context1182                te.exceptions = exceptions1183                if cause:1184                    queue.append((te.__cause__, e.__cause__))1185                if context:1186                    queue.append((te.__context__, e.__context__))1187                if exceptions:1188                    queue.extend(zip(te.exceptions, e.exceptions))1189 1190    @classmethod1191    def from_exception(cls, exc, *args, **kwargs):1192        """Create a TracebackException from an exception."""1193        return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)1194 1195    @property1196    def exc_type(self):1197        warnings.warn('Deprecated in 3.13. Use exc_type_str instead.',1198                      DeprecationWarning, stacklevel=2)1199        return self._exc_type1200 

Showing the first 1,200 of 1746 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai