Repository navigation
Closed
Labels
Description
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
Labels
Projects
- StatusShow more project fieldsDone
Activity
terryjreedy commentedon Oct 4, 2025
Nice. When I pasted
raise ExceptionGroup("", [NameError()])into current 3.15.0a0, I got the same output except for the file name, which is intentionally different in IDLE. I want want to put the body ot theif isinstance(exc, BaseExceptionGroup):clause in a separate function. Will want some unit tests. Will say more later.@Locked-chess-official What code did you look at to write this? In traceback module? In the new REPL code?
Locked-chess-official commentedon Oct 4, 2025
In fact, the frame of the code is mainly from chatGPT, I just modify it based on the output. And it is different in some condition, for example: One BaseExceptionGroup has a exception in the attribute exceptions that its "__cause__" or "__context__" is another BaseExceptionGroup, it prints extraly one line.
[-]Add the support for BaseExceptionGroup in idle[/-][+]IDLE: add support for BaseExceptionGroup[/+]gh-139551: add support for BaseExceptionGroup in IDLE (GH-139563)
gpshead commentedon Apr 12, 2026
cleaned up and merged, thanks for the contribution!
Revert "pythongh-139551: add support for BaseExceptionGroup in IDLE (p…
pythongh-139551: add support for BaseExceptionGroup in IDLE (pythonGH…
[3.13] gh-139551: add support for BaseExceptionGroup in IDLE (GH-139563…
[3.14] gh-139551: add support for BaseExceptionGroup in IDLE (GH-139563…