I made something similar to what you are aiming for but its also made for managing database transactions and errors in a standard way by creating a Response object that then I inspect after.
import functools
import timeit
import traceback
from forms2.structs import Response
import java.lang.Throwable
class handleTransaction(object):
def __init__(self, logger):
self.logger = logger
def __call__(self, inner_func):
"""
We do 'tx' not in kwargs a few times here. If a fucntion has tx=None and is decorated with this, we generate the transatction.
However, sometimes we want to string together multiple transactions into one larger transcation. So by passing it in directly and checking for
'tx' not in kwargs, we only close it for a self contained decorated function. If tx is passed, then the transaction lives on. However,
then you must manually close it.
Look to forsm2.tests.runAllTest for an example of this.
"""
@functools.wraps(inner_func)
def wrapped_func(*args, **kwargs):
start_time = timeit.default_timer()
# A transaction was not provided, we create one
if 'tx' not in kwargs:
transaction_config = forms2.common.TRANSACTION_CONFIG
tx = system.db.beginNamedQueryTransaction(**transaction_config)
kwargs['tx'] = tx
self.logger.info("Starting transaction %s"%(tx))
self.logger.info("Proceeding using transaction %s"%(kwargs['tx']))
try:
result = inner_func(*args, **kwargs)
response = Response(success=True, result=result, error=None,errorType=None)
except Exception, e:
self.logger.error("Python error occured")
self.logger.error(str(e))
self.logger.error(traceback.format_exc())
response = Response(success=False, result=None, error=str(e), errorType='Python')
except java.lang.Throwable, e:
self.logger.error("Java Error Occured")
self.logger.error(str(e.cause))
self.logger.error(traceback.format_exc())
response = Response(success=False, result=None, error=str(e.cause), errorType='Java')
finally:
if response.success:
self.logger.info("Committing transaction %s"%(str(kwargs['tx'])))
system.db.commitTransaction(kwargs['tx'])
else:
self.logger.info("Rolling back transaction %s"%(str(kwargs['tx'])))
system.db.rollbackTransaction(kwargs['tx'])
self.logger.info("Closing Transaciton %s"%(str(kwargs['tx'])))
system.db.closeTransaction(kwargs['tx'])
# At this point we would parse the result object and return an appropraite? Or return entire result
elapsed = timeit.default_timer() - start_time
self.logger.info("Finished transaction in %f seconds"%(elapsed))
return response
return wrapped_func
and response
class Response(object):
def __init__(self, success=False, result=None, error=None, errorType=None):
self.success = success
self.result = result
self.error = error
self.errorType = errorType
def __str__(self):
if self.success:
return "Success: True, Result: %s"%(str(self.result))
elif self.errorType and self.error:
return "Error Type %s Error Text: %s"%(self.errorType, self.error)
else:
return "Success: False, Result: None, Error Type: None, Error Text: None"
Let's me write my functions that only deal with the actual database actions and let this decorator handle all committing/reverting/closing the transaction. Works pretty well in my experience so far.