From b74c2c954ebd70e8ec24ece8f83957c104e38530 Mon Sep 17 00:00:00 2001 From: Alfredo Deza Date: Thu, 5 Mar 2015 14:59:24 -0500 Subject: [PATCH] add a decorator utility module Signed-off-by: Alfredo Deza --- radosgw_agent/util/__init__.py | 1 + radosgw_agent/util/decorators.py | 112 +++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 radosgw_agent/util/decorators.py diff --git a/radosgw_agent/util/__init__.py b/radosgw_agent/util/__init__.py index e69de29..977f6d0 100644 --- a/radosgw_agent/util/__init__.py +++ b/radosgw_agent/util/__init__.py @@ -0,0 +1 @@ +import log diff --git a/radosgw_agent/util/decorators.py b/radosgw_agent/util/decorators.py new file mode 100644 index 0000000..1a063a4 --- /dev/null +++ b/radosgw_agent/util/decorators.py @@ -0,0 +1,112 @@ +import logging +import sys +import traceback +from functools import wraps + + +def catches(catch=None, handler=None, exit=True, handle_all=False): + """ + Very simple decorator that tries any of the exception(s) passed in as + a single exception class or tuple (containing multiple ones) returning the + exception message and optionally handling the problem if it raises with the + handler if it is provided. + + So instead of doing something like this:: + + def bar(): + try: + some_call() + print "Success!" + except TypeError, exc: + print "Error while handling some call: %s" % exc + sys.exit(1) + + You would need to decorate it like this to have the same effect:: + + @catches(TypeError) + def bar(): + some_call() + print "Success!" + + If multiple exceptions need to be caught they need to be provided as a + tuple:: + + @catches((TypeError, AttributeError)) + def bar(): + some_call() + print "Success!" + + If adding a handler, it should accept a single argument, which would be the + exception that was raised, it would look like:: + + def my_handler(exc): + print 'Handling exception %s' % str(exc) + raise SystemExit + + @catches(KeyboardInterrupt, handler=my_handler) + def bar(): + some_call() + + Note that the handler needs to raise its SystemExit if it wants to halt + execution, otherwise the decorator would continue as a normal try/except + block. + + + :param catch: A tuple with one (or more) Exceptions to catch + :param handler: Optional handler to have custom handling of exceptions + :param exit: Raise a ``SystemExit`` after handling exceptions + :param handle_all: Handle all other exceptions via logging. + """ + catch = catch or Exception + logger = logging.getLogger('radosgw_agent') + + def decorate(f): + + @wraps(f) + def newfunc(*a, **kw): + exit_from_catch = False + try: + return f(*a, **kw) + except catch as e: + if handler: + return handler(e) + else: + logger.error(make_exception_message(e)) + + if exit: + exit_from_catch = True + sys.exit(1) + except Exception: # anything else, no need to save the exception as a variable + if handle_all is False: # re-raise if we are not supposed to handle everything + raise + # Make sure we don't spit double tracebacks if we are raising + # SystemExit from the `except catch` block + + if exit_from_catch: + sys.exit(1) + + str_failure = traceback.format_exc() + for line in str_failure.split('\n'): + logger.error("%s" % line) + sys.exit(1) + + return newfunc + + return decorate + +# +# Decorator helpers +# + + +def make_exception_message(exc): + """ + An exception is passed in and this function + returns the proper string depending on the result + so it is readable enough. + """ + if str(exc): + return '%s: %s\n' % (exc.__class__.__name__, exc) + else: + return '%s\n' % (exc.__class__.__name__) + -- 2.47.3