-
-
Save brycepg/b10de7804c77f547ba3b6a32aad94e24 to your computer and use it in GitHub Desktop.
Also, you might be able to clean / compact things a bit by using a decorator class rather than a function and moving TestInfo stuff into the decorator class ( again this would be more applicable if TestInfo was a function attribute rather than a returned object ).
Here is an example of what I mean for the first comment
"""Use decorators to capture exceptions from a procedural call.
Return a structure containing information about the call."""
class TestInfo:
"""Contains data from function call.
Includes status and any exception that occured"""
def __init__(self):
self.exc = None
self.status = None
self.info = None
def __repr__(self):
"""for IPython"""
return str(self.__dict__)
import sys
from functools import wraps
def exc_info(func):
"""Decorator to return status information"""
func.t = TestInfo()
@wraps(func)
def pack_exception(*args, **kwargs):
"""Return function status
Requires test_info kwarg.
Pack the decorated function with an extra argument with test_info obj.
Catch exception to determine test_info status.
Return test_info object"""
try:
return func(*args, **kwargs)
except Error:
func.t.status = "failure"
func.t.exc = sys.exc_info()
else:
func.t.status = "good"
return pack_exception
class Error(Exception):
pass
@exc_info
def bad_parse(dir_, test_name):
"""In this test an exception occurs before the side-effect happens"""
#test_info.info = "Specific info2"
raise Error("Something happened")
print("Did stuff on %s for %s" % (dir_, test_name))
@exc_info
def good_parse(dir_, test_name):
"""This test completes successfully"""
#test_info.info = "Specific information"
print("Did stuff on %s for %s" % (dir_, test_name))
return 5
In the final version I pack the decorated functions return value, along with TestInfo object on return, if the decorated functions return value isn't None
.
In this simplified version, I use the repr
for readability in iPython. I need the TestInfo
object to be returned even if there is a success to affirm that all functions were called without an exception.
I'm not sure how making the decorator a class will help. I need TestInfo
to be a value-object returned to the caller to do analysis(this example is a proof of concept). I can define TestInfo
class inside the decorator function exc_info
however, which I think makes logical sense since nowhere else does it need to be used.
Looks good. One thing I might recommend is changing repr to actually print the traceback. Also, will this work if good_parse returned something? If not, I wonder if it might not be better to simply attach the test_info as an attribute of the function, which could be checked. IE. within the wrapper, call good_parse (which no longer takes a test_info object) and get whatever it returns. Then the user can check good_parse.test_info to see how the last call to it ran.