BrianPugh / cyclopts

Intuitive, easy CLIs based on python type hints.
Apache License 2.0
325 stars 8 forks source link

[Question] Displaying the Traceback #222

Closed pablospe closed 2 months ago

pablospe commented 2 months ago

Is it possible to get a similar output for the trace like in Typer: https://typer.tiangolo.com/tutorial/exceptions/#example-broken-app

BrianPugh commented 2 months ago

For the given program:

from cyclopts import App

app = App()

@app.default
def main(name: str = "morty"):
    print(name + 3)

if __name__ == "__main__":
    app()

Running it gives the bog-standard python stacktrace:

$ python issue-222.py
Traceback (most recent call last):
  File "/Users/brianpugh/projects/cyclopts2/issue-222.py", line 12, in <module>
    app()
  File "/Users/brianpugh/projects/cyclopts2/cyclopts/core.py", line 907, in __call__
    return command(*bound.args, **bound.kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/brianpugh/projects/cyclopts2/issue-222.py", line 8, in main
    print(name + 3)
          ~~~~~^~~
TypeError: can only concatenate str (not "int") to str

If you want the exception to be rich-formatted, you can catch the exception, per Rich's documentation:

from rich.console import Console
from cyclopts import App

console = Console()
app = App(console=console)

@app.default
def main(name: str = "morty"):
    print(name + 3)

if __name__ == "__main__":
    try:
        app()
    except Exception:
        console.print_exception()

This results in:

$ poetry run python issue-222.py
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/brianpugh/projects/cyclopts2/issue-222.py:16 in <module>                                  │
│                                                                                                  │
│   13                                                                                             │
│   14 if __name__ == "__main__":                                                                  │
│   15 │   try:                                                                                    │
│ ❱ 16 │   │   app()                                                                               │
│   17 │   except Exception:                                                                       │
│   18 │   │   console.print_exception()                                                           │
│   19                                                                                             │
│                                                                                                  │
│ /Users/brianpugh/projects/cyclopts2/cyclopts/core.py:907 in __call__                             │
│                                                                                                  │
│    904 │   │   │   │                                                                             │
│    905 │   │   │   │   return asyncio.run(command(*bound.args, **bound.kwargs))                  │
│    906 │   │   │   else:                                                                         │
│ ❱  907 │   │   │   │   return command(*bound.args, **bound.kwargs)                               │
│    908 │   │   except Exception as e:                                                            │
│    909 │   │   │   try:                                                                          │
│    910 │   │   │   │   from pydantic import ValidationError as PydanticValidationError           │
│                                                                                                  │
│ /Users/brianpugh/projects/cyclopts2/issue-222.py:11 in main                                      │
│                                                                                                  │
│    8                                                                                             │
│    9 @app.default                                                                                │
│   10 def main(name: str = "morty"):                                                              │
│ ❱ 11 │   print(name + 3)                                                                         │
│   12                                                                                             │
│   13                                                                                             │
│   14 if __name__ == "__main__":                                                                  │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: can only concatenate str (not "int") to str

Note that you don't actually need to pass the console object into cyclopts.App, but it's good practice because then cyclopts will use the same Console object to print out other CLI-related things like the help page.

pablospe commented 2 months ago

Thanks a lot for the answer!

pablospe commented 2 months ago

Thanks a lot for the answer!