Skip to content

Conversation

Locked-chess-official
Copy link

@Locked-chess-official Locked-chess-official commented Oct 4, 2025

@Locked-chess-official
Copy link
Author

Locked-chess-official commented Oct 4, 2025

Now I have found one different between the IDLE and REPL in this change. However, I don't think it affects:
image

@Locked-chess-official
Copy link
Author

The change that if isinstance(val, BaseExceptionGroup) branch becomes a function alone is difficult because it is a closure function.

@picnixz picnixz changed the title gh-139551: add the support for BaseExceptionGroup gh-139551: support for BaseExceptionGroup in IDLE Oct 5, 2025
@picnixz picnixz changed the title gh-139551: support for BaseExceptionGroup in IDLE gh-139551: add support for BaseExceptionGroup in IDLE Oct 5, 2025
Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests and a What's New entry as well? I think the code could also avoid having all those if prefix: inside the for loops and many times, the additional string is either two spaces or the given prefix.

"another exception occurred:\n", file=efile)
if isinstance(exc, BaseExceptionGroup):
if tb:
if not prefix:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's swap the if to be consistent with the other ifs.

else:
first_line_pre = " "
if not prefix:
print(f" {first_line_pre}+---------------- {i} ----------------", file=efile)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that - is stored as a variable, say sep = '-' * N where N is the number of '-' you want to print. You would also be able to use "prefix2".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in traceback.py use many "-" to print instead of sep = '-' * N: (for example now in 3.13.7)

                # format exception group
                is_toplevel = (_ctx.exception_group_depth == 0)
                if is_toplevel:
                    _ctx.exception_group_depth += 1

                if exc.stack:
                    yield from _ctx.emit(
                        'Exception Group Traceback (most recent call last):\n',
                        margin_char = '+' if is_toplevel else None)
                    yield from _ctx.emit(exc.stack.format(colorize=colorize))

                yield from _ctx.emit(exc.format_exception_only(colorize=colorize))
                num_excs = len(exc.exceptions)
                if num_excs <= self.max_group_width:
                    n = num_excs
                else:
                    n = self.max_group_width + 1
                _ctx.need_close = False
                for i in range(n):
                    last_exc = (i == n-1)
                    if last_exc:
                        # The closing frame may be added by a recursive call
                        _ctx.need_close = True

                    if self.max_group_width is not None:
                        truncated = (i >= self.max_group_width)
                    else:
                        truncated = False
                    title = f'{i+1}' if not truncated else '...'
                    yield (_ctx.indent() +
                           ('+-' if i==0 else '  ') +
                           f'+---------------- {title} ----------------\n')
                    _ctx.exception_group_depth += 1
                    if not truncated:
                        yield from exc.exceptions[i].format(chain=chain, _ctx=_ctx, colorize=colorize)
                    else:
                        remaining = num_excs - self.max_group_width
                        plural = 's' if remaining > 1 else ''
                        yield from _ctx.emit(
                            f"and {remaining} more exception{plural}\n")

                    if last_exc and _ctx.need_close:
                        yield (_ctx.indent() +
                               "+------------------------------------\n")
                        _ctx.need_close = False
                    _ctx.exception_group_depth -= 1

                if is_toplevel:
                    assert _ctx.exception_group_depth == 1
                    _ctx.exception_group_depth = 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I would like you to actually split the lines as it's done, not have everything on a single line (as you can see each group containing - is on its own line).

seen = set()

def print_exc(typ, exc, tb):
def print_exc(typ, exc, tb, prefix=""):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is anyway a local function, I'd suggest having other helpers to reduce the complexity of the function.

else:
print("\nDuring handling of the above exception, "
"another exception occurred:\n", file=efile)
if isinstance(exc, BaseExceptionGroup):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance, here, I would suggest using a separate function, say print_exc_group() that you would define locally as well.

Copy link
Member

@picnixz picnixz Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change that if isinstance(val, BaseExceptionGroup) branch becomes a function alone is difficult because it is a closure function.

Even with that, it's not really important. What is the issue with it being a local function? print_exc is already a local function. Just pass the arguments you need to that local function.

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
@picnixz
Copy link
Member

picnixz commented Oct 5, 2025

More generally, don't we have a helper in traceback for rendering exception groups actually? Can't we use it?

@Locked-chess-official
Copy link
Author

Locked-chess-official commented Oct 5, 2025

More generally, don't we have a helper in traceback for rendering exception groups actually? Can't we use it?

Maybe not. In IDLE, it need traceback.print_list to print the right tb. If we use it, one tb with file "<pyshell#x>" will have no code and there will be a tb exec(code). So the code must be writed alone here.

@picnixz
Copy link
Member

picnixz commented Oct 5, 2025

Ok. For now, let's write it as is, and later we can rewrite the exception rendering logic by using the same approach as for traceback.TracebackException where we use a context for indentation. It could reduce the code complexity (compare the current code with traceback.TracebackException.format)

@Locked-chess-official
Copy link
Author

I have no idea whether to do it. What should be used is traceback._ExceptionPrintContext wich is a private attribute.

@picnixz
Copy link
Member

picnixz commented Oct 5, 2025

No, I meant, we should mimic this ourselves. We may need to tweak the context itself, but the approach can be taken. Not in this PR though I would say as it may be too much changes in one PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants