Intercept exception handling

I have a script I wrote that calls an API, crunches through some data, and displays it as text in the terminal. There is exception handling in the script in case of server problems (here is a cut-down example):

cli_script.py

def api_calls():
    try:
        response = requests.get(login_url, headers=login_headers, verify=False)
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        print("An error occurred: %s, %s" % (
              json.loads(response.content)['title'],
              json.loads(response.content)['detail']))
        exit()
    except requests.exceptions.ConnectionError as e:
        print("Connection error:", str(e))
        exit()

and this works. I am now writing another script that takes that data and puts it into graphs in a GUI, but I want exceptions to show the error in a window:

gui_script.py

from cli_script import api_calls

try:
    results = api_calls()
except exceptions.RequestException as err:
    exception_window(err)

The window is never called, however, as the program exits because of the exception handling in the library it’s calling. Is there any way to force the program to do what I want, or would I be better off moving the try block out of the particular function and into the call, like in the second example?

The problem is that you are calling exit() instead of reraising the RequestException. According to the Python documentation, sys.exit() raises SystemExit, which is a subclass of BaseException. Yet, at gui_script.py you are handling a requests.exceptions.RequestException, so it could never catch the SystemExit.

See if the following solution works for you:

def api_calls():
    try:
        response = requests.get(login_url, headers=login_headers, verify=False)
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        print("An error occurred: %s, %s" % (
              json.loads(response.content)['title'],
              json.loads(response.content)['detail']))
        raise
    except requests.exceptions.ConnectionError as e:
        print("Connection error:", str(e))
        raise

lol

well at least that’s working, I can figure out the formatting. Thanks!

1 Like

Generally, yes. That’s why you have the flexibility: so that you can put the except logic in the place where you are prepared to deal with the exception.

raise by itself, inside an except block, will re-raise the same exception to be used again. This allows multiple places to handle the same exception., as you’ve seen.

In general, trying to “force” existing code to do something it wasn’t designed to do is not what you want. Either work with how it was designed (especially if someone else wrote it), or fix that code (you’ve now learned that the design is not as good as you thought).

In your particular case, it seems like you want gui_script and cli_script to be separate ways to start the program. Each one should use the library functionality (in this case, using the third-party requests library to make a web request) and report errors, in its own way. The natural way to handle this is: move the exception handling for the CLI script, out of the api_calls function, into whatever the caller is for that when you use the CLI script (it might be something at top level, and that’s fine). This way, when the GUI script uses api_calls, it won’t have to worry about the CLI-specific exception handling happening, but the CLI script can still use it. You may also find that there is a better place to put the api_calls function, and have both “main” scripts import it.

1 Like