How create decorator inside a class? - python

I want create a simple decorator function that get in input a func e add some methode before.
Here an example:
class A:
def beforeWriteOracle(func):
def wrapper(self, func):
self.dbOracle.truncateTable(self.oracle_final_table)
func(self.mylist)
return wrapper
#beforeWriteOracle
def writeDataToOracle(self, writeDataToOracleRequestList):
return self.executeMethod(self.writeDataToOracleImpl, writeDataToOracleRequestList, threaded, False)
self.writeDataToOracle(self, writeDataList)
but i have error:
"beforeWriteOracle() missing 1 required positional argument: 'func'"
How use correctly decorator for my case?
thanks

You don't need to (or want to) pass func as an argument to the wrapper; the wrapper should take the same arguments that function you are decorating takes, since it's going to "become" that function.
func itself is available as a non-local variable inside wrapper, which
is a closure: it retains the value passed to beforeWriteOracle even after beforeWriteOracle exits.
def beforeWriteOracle(func):
def wrapper(self, *args, **kwargs):
self.dbOracle.truncateTable(self.oracle_final_table)
func(self, *args, **kwargs)
return wrapper
#beforeWriteOracle
def writeDataToOracle(self, writeDataToOracleRequestList):
return self.executeMethod(self.writeDataToOracleImpl, writeDataToOracleRequestList, threaded, False)

Related

python decorator - is it possible to return a function that expects more params?

I have a really simple function, defined as
def test(x):
return x
I would like to wrap it with a decorator, that returns a function that expects another kwargs param.
#simple_dec
def test(x):
return x
Inside that decorator function, i would pop that param from the kwargs dict, and that just call test function with the params test would expect to get, without breaking it:
def simple_dec():
def simple_dec_logic(func, *args, **kwargs):
kwargs.pop("extra_param")
return func(*args, **kwargs)
return decorator.decorate(_func, simple_dec_logic)
My issue is - after wrapping it, if I call:
test(1, extra_param=2)
It fails on "test got unexpected param extra_param", although if the code would actually run, the decorator would handle this and call test func, without that param. If I get it correctly, the interpreter just fails it, before running the code and knowing it's defined with a decorator.
Is there any way to work around this? I would like the decorator to let me call the test function with more params, without defining them in the test function.
This works fine:
import functools
def decorator(func):
#functools.wraps(func)
def wrapper_decorator(*args, **kwargs):
kwargs.pop('extra_param')
value = func(*args, **kwargs)
return value
return wrapper_decorator
#decorator
def test(x):
return x
test(1, extra_param=2)

Python decorator for accessing class variable

I have a decorator to control time limit, if the function execution exceeds limit, an error is raised.
def timeout(seconds=10):
def decorator(func):
# a timeout decorator
return decorator
And I want to build a class, using the constructor to pass the time limit into the class.
def myClass:
def __init__(self,time_limit):
self.time_limit = time_limit
#timeout(self.time_limit)
def do_something(self):
#do something
But this does not work.
File "XX.py", line YY, in myClass
#timeout(self.tlimit)
NameError: name 'self' is not defined
What's the correct way to implement this?
self.time_limit is only available when a method in an instance of your class is called.
The decorator statement, prefixing the methods, on the other hand is run when the class body is parsed.
However, the inner part of your decorator, if it will always be applied to methods, will get self as its first parameter - and there you can simply make use of any instance attribute:
def timeout(**decorator_parms):
def decorator(func):
def wrapper(self, *args, **kwargs):
time_limit = self.time_limit
now = time.time()
result = func(self, *args, **kwargs)
# code to check timeout
..
return result
return wrapper
return decorator
If your decorator is expected to work with other time limits than always self.limit you could always pass a string or other constant object, and check it inside the innermost decorator with a simple if statement. In case the timeout is a certain string or object, you use the instance attribute, otherwise you use the passed in value;
You can also decorate a method in the constructor:
def myClass:
def __init__(self,time_limit):
self.do_something = timeout(time_limit)(self.do_something)
def do_something(self):
#do something

Wrapping a decorator, with arguments

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.

python using self in decorators in class with kwarg

So I have this set of code, testing decorator base_permissions_check:
def authenticated(self,*arg,**kwargs):
function_call = inspect.stack()[1][4][0].strip()
matched = re.match('^self\.', function_call)
if not matched:
raise Exception("function is private")
return self.user.is_authenticated()
def base_permissions_check(func):
def wrap(self,**kwargs):
if not self.authenticated(kwargs):
return self.permissions
# func(kwargs)
return func(kwargs)
return wrap
#public
#base_permissions_check
def has_video_permission(self,**kwargs):
The error says that authenticated() takes 1 positional argument but 2 were given when I call has_video_permission. I really don't know what's wrong? while I pass only one kwarg into it
The wrapped func() function is not bound to the instance as a method, so you need to pass in self explicitly. You also need to apply the kwargs dictionary as separate keyword arguments using the **kwargs call syntax:
return func(self, **kwargs)
Your authenticated method is not actually using the kwargs dictionary you pass in, so you could just call self.authenticated(), but if you need to have access to those keyword arguments, you probably want to use the **kwargs call syntax there too:
if not self.authenticated(**kwargs):
Since you see the error when you call the decorated function, you are probably passing in a positional argument, but your wrapper only accepts keyword arguments (beyond self). Perhaps you want to add *args to handle those positional arguments:
def base_permissions_check(func):
def wrap(self, *args, **kwargs):
if not self.authenticated(**kwargs):
return self.permissions
# func(kwargs)
return func(self, *args, **kwargs)
return wrap
#public
#base_permissions_check
def has_video_permission(self, *args, **kwargs):
# ...

Decorator not work with argument suggestions?

decorator code:
from functools import wraps
def wrap2(func):
#wraps(func)
def wrap(*args, **kwargs):
return func(*args, **kwargs)
return wrap
test function :
#wrap2
def f2(x='', y=''):
return 1
def f3(x='', y=''):
return 1
problem: can not use arguments suggestion with tab key on decorated function.
screenshot:
great thanks
the decorator can only keep the doctoring the same even if you use functools.wraps, but can not keep the signature of your original function.

Categories