Skip to content

IDLE: add support for BaseExceptionGroup #139551

@Locked-chess-official

Description

@Locked-chess-official

Feature or enhancement

Proposal:

I experimentally change the print_exception() in idlelib.run and it can now print the ExceptionGroup:

# original
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    raise ExceptionGroup("", [NameError()])
ExceptionGroup:  (1 sub-exception)
# now
  + Exception Group Traceback (most recent call last):
  |   File "<pyshell#1>", line 1, in <module>
  |     raise ExceptionGroup("", [NameError()])
  | ExceptionGroup:  (1 sub-exception)
  +-+---------------- 1 ----------------
    | NameError
    +------------------------------------

The code is it:

def print_exception():
    import linecache
    linecache.checkcache()
    flush_stdout()
    efile = sys.stderr
    typ, val, tb = excinfo = sys.exc_info()
    sys.last_type, sys.last_value, sys.last_traceback = excinfo
    sys.last_exc = val
    seen = set()

    def print_exc(typ, exc, tb, prefix=""):
        seen.add(id(exc))
        context = exc.__context__
        cause = exc.__cause__
        exclude = ("run.py", "rpc.py", "threading.py", "queue.py",
                   "debugger_r.py", "bdb.py")
        if cause is not None and id(cause) not in seen:
            print_exc(type(cause), cause, cause.__traceback__, prefix)
            if prefix:
                print(f"{prefix}|\n{prefix}| The above exception was the direct cause "
                      f"of the following exception:\n{prefix}|", file=efile)
            else:
                print("\nThe above exception was the direct cause "
                      "of the following exception:\n", file=efile)
        elif (context is not None and
                not exc.__suppress_context__ and
                id(context) not in seen):
            print_exc(type(context), context, context.__traceback__, prefix)
            if prefix:
                print(f"{prefix}|\n{prefix}| During handling of the above exception, "
                      f"another exception occurred:\n{prefix}|", file=efile)
            else:
                print("\nDuring handling of the above exception, "
                      "another exception occurred:\n", file=efile)
        if isinstance(exc, BaseExceptionGroup):
            if not prefix:
                print("  + Exception Group Traceback (most recent call last):", file=efile)
            else:
                print(f"{prefix}| Exception Group Traceback (most recent call last):", file=efile)
            if tb:
                tbe = traceback.extract_tb(tb)
                cleanup_traceback(tbe, exclude)
                for line in traceback.format_list(tbe):
                    for subline in line.rstrip().splitlines():
                        if not prefix:
                            print(f"  | {subline}", file=efile)
                        else:
                            print(f"{prefix}| {subline}", file=efile)
            lines = get_message_lines(typ, exc, tb)
            for line in lines:
                if not prefix:
                    print(f"  | {line}", end="", file=efile)
                else:
                    print(f"{prefix}| {line}", end="", file=efile)
  
            for i, sub in enumerate(exc.exceptions, 1):
                if i == 1:
                    first_line_pre = "+-"
                else:
                    first_line_pre = "  "
                if not prefix:
                    print(f"  {first_line_pre}+---------------- {i} ----------------", file=efile)
                else:
                    print(f"{prefix}{first_line_pre}+---------------- {i} ----------------", file=efile)
                if id(sub) not in seen:
                    if not prefix:                            
                        print_exc(type(sub), sub, sub.__traceback__, "    ")
                    else:
                        print_exc(type(sub), sub, sub.__traceback__, prefix + "  ")
                    need_print_underline = not isinstance(sub, BaseExceptionGroup)
                else:  # avoid infinitely printing the exception
                    if not prefix:
                        print("f    | <exception {type(sub).__name__} has printed>")
                    else:
                        print(f"{prefix}  | <exception {type(sub).__name__} has printed>")
                    need_print_underline = True
                if need_print_underline:
                    if not prefix:
                        print("    +------------------------------------", file=efile)
                    else:
                        print(f"  {prefix}+------------------------------------", file=efile)
  
        else:
            if tb:
                if prefix:
                    print(f"{prefix}| Traceback (most recent call last):", file=efile)
                else:
                    print("Traceback (most recent call last):", file=efile)
                tbe = traceback.extract_tb(tb)
                cleanup_traceback(tbe, exclude)
                if prefix:
                    for line in traceback.format_list(tbe):
                        for subline in line.rstrip().splitlines():
                            print(f"{prefix}| {subline}", file=efile)
                else:
                    traceback.print_list(tbe, file=efile)
            lines = get_message_lines(typ, exc, tb)
            for line in lines:
                if prefix:
                    print(f"{prefix}| {line}", end="", file=efile)
                else:
                    print(line, end='', file=efile)
  
    print_exc(typ, val, tb)

It is very close to the original python's ExceptionGroup output. However, it may needed more change. For example, how to deal with the print("f | <exception {type(sub).__name__} has printed>")

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytopic-IDLEtype-featureA feature request or enhancement

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions