Is it possible to develop a decorator to determine if a function successfully completed without crashing ? Something like the decorator below but it must detect if the function ran successfully.
def functionStartDecorator():
print("Function has started")
def decoratorWrapper(functionToBeDecorated):
def wrapper(*args, **kwargs):
#Todo log function has run
return functionToBeDecorated(*args, **kwargs)
return wrapper
return decoratorWrapper
As said in comments, simplest would be to wrap your function in a try/except. If your function returns nothing and simply operate by side-effect, it should be straightforward to have the decorated function return the status of that function run:
def tryfn(f):
def decorated(*args, **kwargs):
try:
f(*args, **kwargs)
return 'Ran successfully'
except Exception as e:
return 'Error: {}'.format(e)
return decorated
You can then tweak the exact return type of decorated: maybe return a boolean, maybe append the status of the function to the return of f, maybe log things... The core principle will probably still be around a try/except within the decorated function. For instance, if you want to return both your return value (or None if it failed) and whether this was a success:
def tryfn(f):
def decorated(*args, **kwargs):
try:
res = f(*args, **kwargs)
return True, res
except:
return False, None
return decorated
Note that in this case, you're losing the information about the exact error, so you might want to expand the return to include success, failure, error, etc... that becomes a trade-off between convenience and completeness, which will depend on your exact problem.
I did something like this. Do you see any issues with this code ?
def functionStartDecorator():
def decoratorWrapper(functionToBeDecorated):
def wrapper(*args, **kwargs):
try:
print("Function has started")
result=functionToBeDecorated(*args, **kwargs)
print("Function has complete")
return result
except:
print ("Function failed")
return wrapper
return decoratorWrapper
#functionStartDecorator()
def simplefunc():
return "somthing"
Related
Here is my code:
name = "Arthur"
message = "Hello!"
def decorate(func_to_decorate):
def wrap(*args, **kwargs):
print ("........................")
func_to_decorate(*args, **kwargs)
print ("........................")
return wrap
#decorate
def send_message(your_name="unassigned", your_message="blank"):
print(your_name)
print(your_message)
send_message(name, message)
My error is in line 20:
send_message(name, message)
TypeError: 'NoneType' object is not callable
My understanding is that the wrapper is "replacing" itself with the function immediately following the decorator. This seems work when I am not passing arguments to the function being decorated, but not with the decorator present.
There are two things wrong with your decorator.
First, there's an indentation mistake:
def decorate(func_to_decorate):
def wrap(*args, **kwargs):
print ("........................")
func_to_decorate(*args, **kwargs)
print ("........................")
return wrap
That return wrap is part of the wrap function body, not part of the decorate function body. So, decorate has no return statement, which means it returns None. Hence the error you see: the decorator is in fact "replacing" the wrapped function with the wrapper it returns—but that wrapper is None, so you end up trying to call None as a function.
And meanwhile, you seem to understand that wrap should return something, but that something definitely shouldn't be itself. Usually, what you want to return is the result of the wrapped function (or some post-processed version of that result). In your test, you're only trying to wrap up a function that's used only for side-effects, not to mention that you never get to call the wrapper because of your first problem, so you wouldn't notice this problem yet, but you still want to fix it.
So:
def decorate(func_to_decorate):
def wrap(*args, **kwargs):
print ("........................")
retval = func_to_decorate(*args, **kwargs)
print ("........................")
return retval
return wrap
I'm trying to replace the marshal_with decorator from flask-restful with a decorator that does something before calling marshal_with. My approach is to try to implement a new decorator that wraps marshal_with.
My code looks like:
from flask.ext.restful import marshal_with as restful_marshal_with
def marshal_with(fields, envelope=None):
def wrapper(f):
print("Do something with fields and envelope")
#wraps(f)
def inner(*args, **kwargs):
restful_marshal_with(f(*args, **kwargs))
return inner
return wrapper
Unfortunately this seems to break things... no error messages but my API returns a null response when it shouldn't be. Any insights on what I'm doing wrong?
I don't know the specifics of marshal_with, but it's entirely possible to use multiple decorators on a single function. For instance:
def decorator_one(func):
def inner(*args, **kwargs):
print("I'm decorator one")
func(*args, **kwargs)
return inner
def decorator_two(text):
def wrapper(func):
def inner(*args, **kwargs):
print(text)
func(*args, **kwargs)
return inner
return wrapper
#decorator_one
#decorator_two("I'm decorator two")
def some_function(a, b):
print(a, b, a+b)
some_function(4, 7)
The output this gives is:
I'm decorator one
I'm decorator two
4 7 11
You can modify this little script by adding print statements after each inner function call to see the exact flow control between each decorator as well.
I was doing a couple things wrong here, first, failing to return the output of restful_marshal_with as jonrsharpe pointed out, secondly, failing to understand a decorator written as a class instead of a function, and how to properly pass values to it. The correct code ended up being:
def marshal_with(fields, envelope=None):
def wrapper(f):
print("Do something with fields and envelope")
#wraps(f)
def inner(*args, **kwargs):
rmw = restful_marshal_with(fields, envelope)
return rmw(f)(*args, **kwargs)
return inner
return wrapper
As you can see, in addition to not returning rmw(), I needed to properly initialize the request_marshal_with class before calling it. Finally, it is important to remember that decorators return functions, therefore the arguments of the original function should be passed to the return value of rmw(f), hence the statement return rmw(f)(*args, **kwargs). This is perhaps more apparent if you take a look at the flask_restful.marshal_with code here.
Please consider the following simplified example:
permitted = True
class is_allowed(object):
def __init__(self, some_arg):
# this is actually needed in the complete code
self.some_arg = some_arg
def __call__(self, f):
if permitted == False:
raise Exception("not authenticated to do that")
def wrapped_f(*args, **kwargs):
f(*args, **kwargs)
return wrapped_f
#is_allowed("blah")
def print_hi():
print("hi")
print_hi()
permitted = False
print_hi()
I guess the problem is that the decorator is only called once when the function print_hi() is defined. Because of that the change of the global variable has no effect. Is there any way to circumwent this behaviour?
Move the check inside of the wrapped_f
def __call__(self, f):
def wrapped_f(*args, **kwargs):
if not permitted:
raise Exception("not authenticated to do that")
f(*args, **kwargs)
return wrapped_f
Outside of the wrapped_f, it is checked at the creation of the function. Inside, it becomes part of the body of the new callable, which means it will be checked each time there is a call.
You want to realize that wrapped_f will be called instead of print_hi, so you want any behavior that should be included in the function to go inside of that.
I currently have the following code which uses a python library:
f = Foo(original_method, parameters)
I would like to augment original_method, and have a decorator add a few lines of code. Let's call the new decorated method decorated_method. Finally I would like to have something like this:
f = Foo(decorated_method(original_method), parameters)
My questions are: is this possible? how would the decorator look like?
I must say that I can't extend original_method, since it is part of an external library.
Edit: original_method is not executed, is only passed to Foo as a parameter. decorated_method function should do some logging and gather some statistics of the number of calls.
Later edit: the code in examples below works fine. I had a few additional problems because original_method had a few attributes, so this is the final code:
def decorated_method(method):
def _reporter(*args, **kwargs):
addmetric('apicall', method.__name__)
return method(*args, **kwargs)
_reporter.original_method_attribute = method.original_method_attribute
return _reporter
You don't mention what you want decorated_method to do, but this is certainly possible:
def decorated_method(f):
def _wrapped(*args, **kwargs):
print "About to call f!"
ret = f(*args, **kwargs)
print "Just got finished with f, ret = %r" % (ret,)
return ret
return _wrapped
This is just standard decorator structure: A decorator is a function which accepts a function and returns a function.
Absolutely:
def decorated_method(fn):
def inner_method(*args, **kwargs):
print("before calling")
result = fn(*args, **kwargs)
print("after calling")
return result
return inner_method
Once you've got this working, you should look at signature-preserving decorators.
I wrote a decorator that looks like this
def login_required_message(*args, **kwargs):
kwargs.setdefault('message', "You must be logged in to do that.")
return _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)
But when I try to use it without the () at the end, it fails, unless I rewrite it like this:
def login_required_message(function=None, *args, **kwargs):
kwargs.setdefault('message', "You must be logged in to do that.")
decorator = _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)
if function: return decorator(function)
else: return decorator
Then the () is optional. So, how do I encapsulate this "optional" functionality into a decorator so that I can decorate my decorators to allow no arguments?
I actually recently wrote a blog post about this - at least, I think it addresses what you're trying to do. For posterity, here's what I came up with:
def opt_arguments(func):
def meta_wrapper(*args, **kwargs):
if len(args) == 1 and callable(args[0]):
return func(args[0])
else:
def meta_func(inner_func):
return func(inner_func, *args, **kwargs)
return meta_func
return meta_wrapper
By the way, in the process of trying to figure out how to do it, I came to the conclusion that this is one of those things that is almost always more complication than necessary ;-)
This answer is inspired by recipe 9.6 of the Python Cookbook
def mydecorator_with_opt_args(func=None, arg1="arg1-default", arg2="arg2-default"):
if func is None:
return partial(mydecorator_with_opt_args, arg1=arg1, arg2=arg2)
#wraps(func)
def wrapper(*args, **kwargs):
print(f"Do something with {arg1} and {arg2}")
return func(*args, **kwargs)
return wrapper
To use it :
#mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")
def passer():
pass
passer()
will output
"Do something with arg1-set and arg2-set"
#mydecorator_with_opt_args
def passer2():
pass
passer2()
will output
"Do something with arg1-default and arg2-default"
To understand what happens if you specify no args (passer2):
Putting a decorator is the same as writing passer2 = mydecorator_with_opt_args(passer2) which means the if func is None is ignored and you apply the decorator with the default arguments
However if you specify args (passer), Putting a decorator is the same as writing passer = mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")(passer)
mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set") returns partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set") and then partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set")(passer) is equivalent to mydecorator_with_opt_args(func=passer, arg1="arg1-set", arg2="arg2-set")
Note that you need to specify the keyword arguments.