Some of my functions use a "fail_silently" flag. It is used in the following way:
def test(a, b, c=1, fail_silently = False) try: return int(a) + c except: if fail_silently: return None raise
Therefore, if there is an error, we catch it and fail gracefully. The key here is that it can be toggled dynamically by whoever is calling it.
I am using this for a many different functions and class methods and thought to make it a decorator.
There are a few problems:
- I want to be able name the flag "raise_exception" or "fail_silently" or "whatever"...
- The flag may or may not have a default value
- The flag may or may not be passed in (usually not)
- It needs to work with class methods too
Thus my decorator needs to look for (assuming the flag is called "fail_silently") the flag in the following locations in this order
- **kwargs (the passed in function arguments), simple dictionary get on flag
- *args - get the positional argument of the flag, then scan for index in args (which might not be there)
- Get the default value of the flag from the function
The problem is the code is now getting really messy and has many points of failure.
def try_except_response(parameter = 'fail_silently'): def real_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) # the function itself, except: # if it raises an Exception! # check to see if the Flag is in passed in kwargs! if kwargs.get(parameter)== True: return None elif kwargs.get(parameter) == False: raise else: # Flag is not in kwargs, check to see if it is in args function_args, vargs, kewords, defaults = inspect.getargspec(func) # get the index of argument of interest try: argument_index = function_args.index(parameter) # get the index of argument of interest except ValueError: raise ValueError('The inputted decorator for "fail_silently" was not found! The behavior will not work.') if len(args) > argument_index: # check to see if it is inputted keyword = args[argument_index] # get the value!! if keyword == True: return None elif kwargs == False: raise else: raise ValueError('The "fail_silently" flag did not return a value that we understand. Must be True or False') else: # not in args either! let's go get the default value. # TODO MORE STUFF!!! raise raise raise return wrapper
What is the best way this can be cleaned up? Or alternatives? I am thinking about implementing a version of locals() that will create a dictionary of all parameters passed into the function and then check from there...
There are simplifying assumptions we can make, but that would require the person writing the function to know something ('flag is always last argument, or default value is always False...'), but I am hoping to get the decorator to be as transparent as possible so it just "works" and lets the function be a function (or class method)