Python Global Exception Handling

Python global exception handling

You could change sys.excepthook if you really don't want to use a try/except.

import sys
def my_except_hook(exctype, value, traceback):
if exctype == KeyboardInterrupt:
print "Handler code goes here"
else:
sys.__excepthook__(exctype, value, traceback)
sys.excepthook = my_except_hook

Global error handler for any exception

You can use @app.errorhandler(Exception):

Demo (the HTTPException check ensures that the status code is preserved):

from flask import Flask, abort, jsonify
from werkzeug.exceptions import HTTPException

app = Flask('test')

@app.errorhandler(Exception)
def handle_error(e):
code = 500
if isinstance(e, HTTPException):
code = e.code
return jsonify(error=str(e)), code

@app.route('/')
def index():
abort(409)

app.run(port=1234)

Output:

$ http get http://127.0.0.1:1234/
HTTP/1.0 409 CONFLICT
Content-Length: 31
Content-Type: application/json
Date: Sun, 29 Mar 2015 17:06:54 GMT
Server: Werkzeug/0.10.1 Python/3.4.3

{
"error": "409: Conflict"
}

$ http get http://127.0.0.1:1234/notfound
HTTP/1.0 404 NOT FOUND
Content-Length: 32
Content-Type: application/json
Date: Sun, 29 Mar 2015 17:06:58 GMT
Server: Werkzeug/0.10.1 Python/3.4.3

{
"error": "404: Not Found"
}

If you also want to override the default HTML exceptions from Flask (so that they also return JSON), add the following before app.run:

from werkzeug.exceptions import default_exceptions
for ex in default_exceptions:
app.register_error_handler(ex, handle_error)

For older Flask versions (<=0.10.1, i.e. any non-git/master version at the moment), add the following code to your application to register the HTTP errors explicitly:

from werkzeug import HTTP_STATUS_CODES
for code in HTTP_STATUS_CODES:
app.register_error_handler(code, handle_error)

Flask global exception handling

I tend to set up an error handler on the app that formats the exception into a json response. Then you can create custom exceptions like UnauthorizedException...

class Unauthorized(Exception):
status_code = 401

@app.errorhandler(Exception)
def _(error):

trace = traceback.format_exc()
status_code = getattr(error, 'status_code', 400)
response_dict = dict(getattr(error, 'payload', None) or ())
response_dict['message'] = getattr(error, 'message', None)
response_dict['traceback'] = trace

response = jsonify(response_dict)
response.status_code = status_code
traceback.print_exc(file=sys.stdout)
return response

You can also handle specific exceptions using this pattern...

@app.errorhandler(ValidationError)
def handle_validation_error(error):
# Do something...

Error handlers get attached to the app, not the apimanager. You probably have something like

app = Flask()
apimanager = ApiManager(app)
...

Put this somewhere using that app object.

Global error handling for a better KeyError treatment

Here is the solution I used right now, since we can't override the builtins.

class LXKeyError(KeyError):
def __init__(self, item, d):
self.item = item
self.d = d

def __str__(self):
keys = str(list(self.d.keys()))

if len(keys) > 1000: # (13 (max field length in uf) + 4 ("', '")) * 50 (max number of fields we want to print) = 850
keys = f'{keys[:995]} ...'

return f'Unknown key in dict: {self.item!r} (keys: {keys})'

class LXDict(dict):
def __missing__(self, key):
raise LXKeyError(key, self)

Test:

orig_d = {'a': 'a', 'b': 'b', '1': '', '2': '', '3': '', '4': '', '5': '', '6': '', '7': '', '8': '', '9': '', '10': '', '11': '', '12': '',
'13': '', '14': '', '15': '', '16': '', '17': '', '18': '', '19': '', '20': '',
}
d = LXDict(orig_d)
try:
d['x']
assert False, f'Should not arrive here'
except KeyError as ex:
print(ex)

Catch `Exception` globally in FastAPI

In case you want to capture all unhandled exceptions (internal server error), there's a very simple way of doing it. Documentation

from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import Response

app = FastAPI()

async def catch_exceptions_middleware(request: Request, call_next):
try:
return await call_next(request)
except Exception:
# you probably want some kind of logging here
return Response("Internal server error", status_code=500)

app.middleware('http')(catch_exceptions_middleware)

Make sure you place this middleware before everything else.

Global Exception Handling in Google App Engine

If you're using the webapp framework, you should already be defining a subclass of RequestHandler that serves as a base class, with all your app's handlers extending that. You can simply override handle_exception, which serves as a global exception handler for any uncaught exceptions.

The default implementation calls self.error(500), logs the exception, and if debug is on, outputs a stacktrace.

If you're using another framework, you could write a piece of WSGI middleware that calls the wrapped WSGI app, and catches any thrown exceptions, dealing with them as you wish.

Exceptions for the whole class

Write one or more exception handler functions that, given a function and the exception raised in it, does what you want to do (e.g. displays an alert). If you need more than one, write them.

def message(func, e):
print "Exception", type(e).__name__, "in", func.__name__
print str(e)

Now write a decorator that applies a given handler to a called function:

import functools

def handle_with(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda f, e: None
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exceptions or Exception as e:
return handler(func, e)
else:
e = None
finally:
cleanup(func, e)
return wrapper
return decorator

This only captures the exceptions you specify. If you don't specify any, Exception is caught. Additionally, the first argument can be a tuple (or other sequence) of two handler functions; the second handler, if given, is called in a finally clause. The value returned from the primary handler is returned as the value of the function call.

Now, given the above, you can write:

@handle_with(message, TypeError, ValueError)
def add(x, y):
return x + y

You could also do this with a context manager:

from contextlib import contextmanager 

@contextmanager
def handler(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda e: None
try:
yield
except exceptions or Exception as e:
handler(e)
else:
e = None
finally:
cleanup(e)

Now you can write:

def message(e):
print "Exception", type(e).__name__
print str(e)

def add(x, y):
with handler(message, TypeError, ValueError):
return x + y

Note that the context manager doesn't know what function it's in (you can find this out, sorta, using inspect, though this is "magic" so I didn't do it) so it gives you a little less useful information. Also, the context manager doesn't give you the opportunity to return anything in your handler.



Related Topics



Leave a reply



Submit