Skip to content

[REQUEST] rich.traceback should handle ExceptionGroup and __notes__ #2238

@Zac-HD

Description

@Zac-HD

I'm pretty excited to start using PEP-654 ExceptionGroup and PEP-678 annotated exceptions in all my code (via the exceptiongroup backport, for now).

The only downside is that tools like rich.traceback (and Pytest) don't yet understand how to display these attributes - hence this feature request! I'd love to for the ecosystem to implement these new features, so that most people will have annotated-exceptiongroup-aware tooling installed by the time Python 3.11 final is released in October 😁

Repro script and output comparison between Rich and native tracebacks
from exceptiongroup import ExceptionGroup  # backport; this is builtin from Python 3.11.0b1

def f():
    raise ValueError("From f()")

def g():
    raise RuntimeError("From g()")

def main():
    excs = []
    for callback in [f, g]:
        try:
            callback()
        except Exception as err:
            err.__notes__ = ["Added a note"]
            excs.append(err)
    if excs:
        raise ExceptionGroup("Oops", excs)

def test():
    try:
        main()
    except Exception as err:
        err.__notes__ = ["Added a note"]
        raise

if __name__ == "__main__":
    from rich.console import Console

    console = Console()
    try:
        test()
    except Exception:
        console.print_exception(show_locals=True)

    print("\n\n" + "=" * 70 + "\n\n")
    test()  # Show the native exception display
$ python repro.py 
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│                                                                                                  │
│ /mnt/c/Users/zacha/Documents/GitHub/hypothesis/repro.py:38 in <module>                           │
│                                                                                                  │
│   35 │   console = Console()                                                                     │
│   36 │                                                                                           │
│   37 │   try:                                                                                    │
│ ❱ 38 │   │   test()                                                                              │
│   39 │   except Exception:                                                                       │
│   40 │   │   console.print_exception(show_locals=True)                                           │
│   41                                                                                             │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮     │
│ │ __annotations__ = {}                                                                     │     │
│ │    __builtins__ = <module 'builtins' (built-in)>                                         │     │
│ │      __cached__ = None                                                                   │     │
│ │         __doc__ = None                                                                   │     │
│ │        __file__ = 'repro.py'                                                             │     │
│ │      __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0x7fdb0c6744c0> │     │
│ │        __name__ = '__main__'                                                             │     │
│ │     __package__ = None                                                                   │     │
│ │        __spec__ = None                                                                   │     │
│ │         Console = <class 'rich.console.Console'>                                         │     │
│ │         console = <console width=138 ColorSystem.EIGHT_BIT>                              │     │
│ │  ExceptionGroup = <class 'exceptiongroup.ExceptionGroup'>                                │     │
│ │               f = <function f at 0x7fdb0bccd9d0>                                         │     │
│ │               g = <function g at 0x7fdb0bccda60>                                         │     │
│ │            main = <function main at 0x7fdb0bccdb80>                                      │     │
│ │            test = <function test at 0x7fdb0bc83790>                                      │     │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯     │
│ /mnt/c/Users/zacha/Documents/GitHub/hypothesis/repro.py:26 in test                               │
│                                                                                                  │
│   23                                                                                             │
│   24 def test():                                                                                 │
│   25 │   try:                                                                                    │
│ ❱ 26 │   │   main()                                                                              │
│   27 │   except Exception as err:                                                                │
│   28 │   │   err.__notes__ = ["Added a note"]                                                    │
│   29 │   │   raise                                                                               │
│                                                                                                  │
│ /mnt/c/Users/zacha/Documents/GitHub/hypothesis/repro.py:21 in main                               │
│                                                                                                  │
│   18 │   │   │   err.__notes__ = ["Added a note"]                                                │
│   19 │   │   │   excs.append(err)                                                                │
│   20 │   if excs:                                                                                │
│ ❱ 21 │   │   raise ExceptionGroup("Oops", excs)                                                  │
│   22                                                                                             │
│   23                                                                                             │
│   24 def test():                                                                                 │
│                                                                                                  │
│ ╭─────────────────────────── locals ────────────────────────────╮                                │
│ │ callback = <function g at 0x7fdb0bccda60>                     │                                │
│ │     excs = [ValueError('From f()'), RuntimeError('From g()')] │                                │
│ ╰───────────────────────────────────────────────────────────────╯                                │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ExceptionGroup: Oops (2 sub-exceptions)


======================================================================


  + Exception Group Traceback (most recent call last):
  |   File "repro.py", line 43, in <module>
  |     test()
  |   File "repro.py", line 26, in test
  |     main()
  |   File "repro.py", line 21, in main
  |     raise ExceptionGroup("Oops", excs)
  | exceptiongroup.ExceptionGroup: Oops (2 sub-exceptions)
  | Added a note
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "repro.py", line 16, in main
    |     callback()
    |   File "repro.py", line 5, in f
    |     raise ValueError("From f()")
    | ValueError: From f()
    | Added a note
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "repro.py", line 16, in main
    |     callback()
    |   File "repro.py", line 9, in g
    |     raise RuntimeError("From g()")
    | RuntimeError: From g()
    | Added a note
    +------------------------------------

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions