Rich Formatted Exceptions
Tracebacks of uncaught exceptions provide valuable feedback for debugging. This guide demonstrates how to enhance your error messages using rich formatting.
Standard Python Traceback
Consider the following example:
from cyclopts import App
app = App()
@app.default
def main(name: str):
print(name + 3)
if __name__ == "__main__":
app()
Running this script will produce a standard Python traceback:
$ python my-script.py foo
Traceback (most recent call last):
File "/cyclopts/my-script.py", line 12, in <module>
app()
File "/cyclopts/cyclopts/core.py", line 903, in __call__
return command(*bound.args, **bound.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/cyclopts/my-script.py", line 8, in main
print(name + 3)
~~~~~^~~
TypeError: can only concatenate str (not "int") to str
Rich Formatted Traceback
To create a more visually appealing and informative traceback, you can use the Rich library's traceback handler. Here's how to modify your script:
import sys
from cyclopts import App
from rich.console import Console
console = Console()
app = App(console=console) # Use same Console object for Cyclopts operations.
@app.default
def main(name: str):
print(name + 3)
if __name__ == "__main__":
try:
app()
except Exception:
console.print_exception()
sys.exit(1)
Now, running the updated script will display a rich-formatted traceback:
$ python my-script.py foo
╭──────────────── Traceback (most recent call last) ─────────────────╮
│ /cyclopts/my-script.py:16 in <module> │
│ │
│ 13 │
│ 14 if __name__ == "__main__": │
│ 15 │ try: │
│ ❱ 16 │ │ app() │
│ 17 │ except Exception: │
│ 18 │ │ console.print_exception(width=70) │
│ 19 │
│ │
│ /cyclopts/cyclopts/core.py:903 in __call__ │
│ │
│ 900 │ │ │ │ │
│ 901 │ │ │ │ return asyncio.run(command(*bound.args, **b │
│ 902 │ │ │ else: │
│ ❱ 903 │ │ │ │ return command(*bound.args, **bound.kwargs) │
│ 904 │ │ except Exception as e: │
│ 905 │ │ │ try: │
│ 906 │ │ │ │ from pydantic import ValidationError as Pyd │
│ │
│ /cyclopts/my-script.py:11 in main │
│ │
│ 8 │
│ 9 @app.default │
│ 10 def main(name: str): │
│ ❱ 11 │ print(name + 3) │
│ 12 │
│ 13 │
│ 14 if __name__ == "__main__": │
╰────────────────────────────────────────────────────────────────────╯
This rich-formatted traceback provides a more readable and visually appealing representation of the error, but may make copy/pasting for sharing a bit more cumbersome.