I love Python's clarity and expressive power but I recently had a problem where my code was becoming excessively repetitive. In short, I was using an API which may raise a number of exceptions (HttpError, ServerNotResponding…) which I needed to report. Other errors are raised as normal. Thus I had lots of blocks of code looking something like this:
try: BLOCK return HttpResponseRedirect('somewhere good') except (HttpError, ServerNotResponding...) as e: return HttpResponse(str(e), content_type="text/plain")
I used a context manager to first collate all of the errors I know how to handle into a single 'aggregation' error type (I used the API base error class but it could have been anything):
class APIError(object): """ Context manager that collects all of the request and other api errors into a single convenient APIError exception (which has an original_exception attribute). Thus: try: with proxy.APIError(): BLOCK except APIError: BLOCK will handle all APIErrors. """ def __init__(self): import inspect self.exc_types = [exc_type for (exc_name, exc_type) in inspect.getmembers(exceptions, callable) if not isinstance(exc_type, exceptions.APIError)] def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if (exc_type in self.exc_types): raise exceptions.APIError(exc_value)
Then I created a decorator that used this context manager thus:
def api_errors_as_http_response(func): @wraps(func) def wrapper(*args, **kwargs): try: with APIError(): return func(*args, **kwargs) except APIError as e: return HttpResponse(str(e), content_type="text/plain") return wrapper
Now, in my Django views I can just decorate any view function with the decorator in order to have all the remote API errors reported as plain text:
@api_errors_as_http_response def add_app(request): ...
All the error handling is nicely described in the decorator and it's just one line to add to any function definition so that it will report the relevant set of errors appropriately. In the event of more complex cases arising, the context manager can be used directly.
Australia: 07 3103 2894
International: +61 410 545 357