Skip to content

[QUESTION] Disable traceback globally on production #525

Description

@moracabanas

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find it.
  • I searched the Typer documentation, with the integrated search.
  • I already searched in Google "How to X in Typer" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to Typer but to Click.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

import typer
import requests
from rich import print
import logging
from rich.logging import RichHandler

logging.basicConfig(
    level="DEBUG",
    format="%(message)s",
    datefmt="[%X]",
    handlers=[RichHandler(rich_tracebacks=True)],
)

log = logging.getLogger("rich")

app = typer.Typer(pretty_exceptions_show_locals=False)

greetings_login = "Login with your Portainer Url, Username and Password to deploy projects to your Portainer i.e.: https://my-portainer.example.com"

@app.command()
def login(
    url:      str = typer.Option(..., prompt=f'{greetings_login}\nUrl'), 
    username: str = typer.Option(..., prompt=True),
    password: str = typer.Option(..., prompt=True, hide_input=True)
):
    url = f'{url}/api/auth'

    payload = {
        "Username": username,
        "Password": password
    }

    try:
        response = requests.request("POST", url, json=payload)
        if (response.status_code == 200):
            print('[green]Successful login ✅[/green] saving credentials...')
            with open("./.jwt", "w") as f:
                f.write(response.text)
        else:
            print(f'Error: {response.reason}')
    except Exception as e:
        log.exception(e)
    
  
@app.command()
def logout(
    force: bool = typer.Option(..., prompt="Are you sure you want to log out?"),
):
    if force:
        print(f"Deleting credentials")
        #TODO implement check .jwt exist then delete
    else:
        print("Operation cancelled")


if __name__ == "__main__":
    app()

Description

I am noob at python trying to write a Portainer CLI client inspired by docker CLI, for learning purposes and speed up personal deployments.

The code shown is just my first try at create a persisted login based on a .jwt local file once a succesful 200 response (and etc... future checks)

Obviously there are tons of unhandled errors and you might notice I am a completely uneducated illiterate when it comes to error handling. My application will burst in imaginative ways I am not even prepared to unit test them

Anyway, when I am checking the login() flow, I test it for breaking inputs like bad Url, bad credentials etc...
Then I got several raised errors from requests or whatever imported lib which is giving up:

[21:07:29] ERROR    Invalid URL 'asdfasdf/api/auth': No scheme supplied. Perhaps you meant http://asdfasdf/api/auth?                   main.py:52
                    ╭────────────────────────────────────── Traceback (most recent call last) ───────────────────────────────────────╮           
                    │ /home/becareo/passbolt/main.py:44 in login                                                                     │           
                    │                                                                                                                │           
                    │   41 │   }                                                                                                     │           
                    │   42 │                                                                                                         │           
                    │   43 │   try:                                                                                                  │           
                    │ ❱ 44 │   │   response = requests.request("POST", url, json=payload)                                            │           
                    │   45 │   │   if (response.status_code == 200):                                                                 │           
                    │   46 │   │   │   print('[green]Successful login ✅[/green] saving credentials...')                             │           
                    │   47 │   │   │   with open("./.jwt", "w") as f:                                                                │           
                    │                                                                                                                │           
                    │ /home/becareo/passbolt/.venv/lib/python3.11/site-packages/requests/api.py:59 in request                        │           
                    │                                                                                                                │           
                    │    56 │   # avoid leaving sockets open which can trigger a ResourceWarning in some                             │           
                    │    57 │   # cases, and look like a memory leak in others.                                                      │           
                    │    58 │   with sessions.Session() as session:                                                                  │           
                    │ ❱  59 │   │   return session.request(method=method, url=url, **kwargs)                                         │           
                    │    60                                                                                                          │           
                    │    61                                                                                                          │           
                    │    62 def get(url, params=None, **kwargs):                                                                     │           
                    │                                                                                                                │           
                    │ /home/becareo/passbolt/.venv/lib/python3.11/site-packages/requests/sessions.py:573 in request                  │           
                    │                                                                                                                │           
                    │   570 │   │   │   cookies=cookies,                                                                             │           
                    │   571 │   │   │   hooks=hooks,                                                                                 │           
                    │   572 │   │   )                                                                                                │           
                    │ ❱ 573 │   │   prep = self.prepare_request(req)                                                                 │           
                    │   574 │   │                                                                                                    │           
                    │   575 │   │   proxies = proxies or {}                                                                          │           
                    │   576                                                                                                          │           
                    │                                                                                                                │           
                    │ /home/becareo/passbolt/.venv/lib/python3.11/site-packages/requests/sessions.py:484 in prepare_request          │           
                    │                                                                                                                │           
                    │   481 │   │   │   auth = get_netrc_auth(request.url)                                                           │           
                    │   482 │   │                                                                                                    │           
                    │   483 │   │   p = PreparedRequest()                                                                            │           
                    │ ❱ 484 │   │   p.prepare(                                                                                       │           
                    │   485 │   │   │   method=request.method.upper(),                                                               │           
                    │   486 │   │   │   url=request.url,                                                                             │           
                    │   487 │   │   │   files=request.files,                                                                         │           
                    │                                                                                                                │           
                    │ /home/becareo/passbolt/.venv/lib/python3.11/site-packages/requests/models.py:368 in prepare                    │           
                    │                                                                                                                │           
                    │    365 │   │   """Prepares the entire request with the given parameters."""                                    │           
                    │    366 │   │                                                                                                   │           
                    │    367 │   │   self.prepare_method(method)                                                                     │           
                    │ ❱  368 │   │   self.prepare_url(url, params)                                                                   │           
                    │    369 │   │   self.prepare_headers(headers)                                                                   │           
                    │    370 │   │   self.prepare_cookies(cookies)                                                                   │           
                    │    371 │   │   self.prepare_body(data, files, json)                                                            │           
                    │                                                                                                                │           
                    │ /home/becareo/passbolt/.venv/lib/python3.11/site-packages/requests/models.py:439 in prepare_url                │           
                    │                                                                                                                │           
                    │    436 │   │   │   raise InvalidURL(*e.args)                                                                   │           
                    │    437 │   │                                                                                                   │           
                    │    438 │   │   if not scheme:                                                                                  │           
                    │ ❱  439 │   │   │   raise MissingSchema(                                                                        │           
                    │    440 │   │   │   │   f"Invalid URL {url!r}: No scheme supplied. "                                            │           
                    │    441 │   │   │   │   f"Perhaps you meant http://{url}?"                                                      │           
                    │    442 │   │   │   )                                                                                           │           
                    ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯           
                    MissingSchema: Invalid URL 'asdfasdf/api/auth': No scheme supplied. Perhaps you meant http://asdfasdf/api/auth? 

I will pack this CLI with pyinstaller hiding its implementation and most important thing, hiding stressful tracebacks for the average user, preserving usefull error like the last raised one:

MissingSchema: Invalid URL 'asdfasdf/api/auth': No scheme supplied. Perhaps you meant http://asdfasdf/api/auth?

I have searched for the correct way to preserve useful errors for the user, hiding the tracebacks and I have come across sys.excepthook exception handlers with no luck.

Tracebacks are enabled with any code hack I've tried. I am a bit lost with providing a usefull error handling for the user here.

Would be awesome if someone more experience could point me to the right direction with useful tips about this

Operating System

Linux, Windows

Operating System Details

Windows 11
WSL2
Poetry

Typer Version

extras = ["all"], version = "^0.7.0"

Python Version

3.11.0

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionQuestion or problem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions