Python decorator within function, needs class within method? - python

Going off of How can I define decorator method inside class? and Accessing self within decorator function within a Python class, I have the following code:
class Custom():
def __init__(self, var):
self.var = var
def renew(func):
#wraps(func)
def wrapper(self, *args, **kwargs):
try:
return func(*args, **kwargs)
except:
print('refreshing')
self.refresh()
return func(*args, **kwargs)
return wrapper
def refresh(self):
self.var = 'refreshed'
#renew
def get_something(self):
print(self.var)
raise Exception
test = Custom('a')
test.get_something(test)
Which returns exactly what I want (pretend that the raise Exception is a expiration timeout, where var expires after some time, am just forcing an Exception here):
a
refreshing
refreshed
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
...
However, is there any way to not require the class instance test to be in the class method, namely test.get_something(test) -> test.get_something()? Or is this best practice? What is the best way to handle something like this?

In your renew decorator you are not passing instance parameter when calling the decorated function from wrapper. Wherever you want to call your func from wrapper, call it like this:
func(self, *args, **kwargs)
Once you correct the invocation call inside wrapper, you no longer need to pass class instance object when invoking your decorated function. So test.get_something() would work now.

Related

How create decorator inside a class?

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)

self lost when using partial inside a decorator

I'm trying to code a method from a class that uses a decorator from another class. The problem is that I need information stored in the Class that contains the decorator (ClassWithDecorator.decorator_param). To achieve that I'm using partial, injecting self as the first argument, but when I do that the self, from the class that uses the decorator " gets lost" somehow and I end up getting an error. Note that this does not happen if I remove partial() from my_decorator() and "self" will be correctly stored inside *args.
See the code sample:
from functools import partial
class ClassWithDecorator:
def __init__(self):
self.decorator_param = "PARAM"
def my_decorator(self, decorated_func):
def my_callable(ClassWithDecorator_instance, *args, **kwargs):
# Do something with decorator_param
print(ClassWithDecorator_instance.decorator_param)
return decorated_func(*args, **kwargs)
return partial(my_callable, self)
decorator_instance = ClassWithDecorator()
class WillCallDecorator:
def __init__(self):
self.other_param = "WillCallDecorator variable"
#decorator_instance.my_decorator
def decorated_method(self):
pass
WillCallDecorator().decorated_method()
I get
PARAM
Traceback (most recent call last):
File "****/decorator.py", line 32, in <module>
WillCallDecorator().decorated_method()
File "****/decorator.py", line 12, in my_callable
return decorated_func(*args, **kwargs)
TypeError: decorated_method() missing 1 required positional argument: 'self'
How can I pass the self corresponding to WillCallDecorator() into decorated_method() but at the same time pass information from its own class to my_callable() ?
It seems that you may want to use partialmethod instead of partial:
From the docs:
class functools.partialmethod(func, /, *args, **keywords)
When func is a non-descriptor callable, an appropriate bound method is created dynamically. This behaves like a normal Python function when used as a method: the self argument will be inserted as the first positional argument, even before the args and keywords supplied to the partialmethod constructor.
So much simpler just to use the self variable you already have. There is absolutely no reason to be using partial or partialmethod here at all:
from functools import partial
class ClassWithDecorator:
def __init__(self):
self.decorator_param = "PARAM"
def my_decorator(self, decorated_func):
def my_callable(*args, **kwargs):
# Do something with decorator_param
print(self.decorator_param)
return decorated_func(*args, **kwargs)
return my_callable
decorator_instance = ClassWithDecorator()
class WillCallDecorator:
def __init__(self):
self.other_param = "WillCallDecorator variable"
#decorator_instance.my_decorator
def decorated_method(self):
pass
WillCallDecorator().decorated_method()
Also, to answer your question about why your code didn't work, when you access something.decorated_method() the code checks whether decorated_method is a function and if so turns it internally into a call WillCallDecorator.decorated_method(something). But the value returned from partial is a functools.partial object, not a function. So the class lookup binding won't happen here.
In more detail, something.method(arg) is equivalent to SomethingClass.method.__get__(something, arg) when something doesn't have an attribute method and its type SomethingClass does have the attribute and the attribute has a method __get__ but the full set of steps for attribute lookup is quite complicated.

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

Python: How do you intercept a method call to change function parameters?

What I am trying to do is write a wrapper around another module so that I can transform the parameters that are being passed to the methods of the other module. That was fairly confusing, so here is an example:
import somemodule
class Wrapper:
def __init__(self):
self.transforms = {}
self.transforms["t"] = "test"
# This next function is the one I want to exist
# Please understand the lines below will not compile and are not real code
def __intercept__(self, item, *args, **kwargs):
if "t" in args:
args[args.index("t")] = self.transforms["t"]
return somemodule.item(*args, **kwargs)
The goal is to allow users of the wrapper class to make simplified calls to the underlying module without having to rewrite all of the functions in the module. So in this case if somemodule had a function called print_uppercase then the user could do
w = Wrapper()
w.print_uppercase("t")
and get the output
TEST
I believe the answer lies in __getattr__ but I'm not totally sure how to use it for this application.
__getattr__ combined with defining a function on the fly should work:
# somemodule
def print_uppercase(x):
print(x.upper())
Now:
from functools import wraps
import somemodule
class Wrapper:
def __init__(self):
self.transforms = {}
self.transforms["t"] = "test"
def __getattr__(self, attr):
func = getattr(somemodule, attr)
#wraps(func)
def _wrapped(*args, **kwargs):
if "t" in args:
args = list(args)
args[args.index("t")] = self.transforms["t"]
return func(*args, **kwargs)
return _wrapped
w = Wrapper()
w.print_uppercase('Hello')
w.print_uppercase('t')
Output:
HELLO
TEST
I would approach this by calling the intercept method, and entering the desired method to execute, as a parameter for intercept. Then, in the intercept method, you can search for a method with that name and execute it.
Since your Wrapper object doesn't have any mutable state, it'd be easier to implement without a class. Example wrapper.py:
def func1(*args, **kwargs):
# do your transformations
return somemodule.func1(*args, **kwargs)
Then call it like:
import wrapper as w
print w.func1('somearg')

Decorators in class methods: compatibility with 'getattr'

I need to make wrappers for class methods, to be executed before and/or after the call of a specific method.
Here is a minimal example:
class MyClass:
def call(self, name):
print "Executing function:", name
getattr(self, name)()
def my_decorator(some_function):
def wrapper():
print("Before we call the function.")
some_function()
print("After we call the function.")
return wrapper
#my_decorator
def my_function(self):
print "My function is called here."
engine = MyClass()
engine.call('my_function')
This gives me an error at the line getattr(self, name)():
TypeError: 'NoneType' object is not callable
If I comment out the decorator before the class method, it works perfectly:
class MyClass:
def call(self, name):
print "Executing function:", name
getattr(self, name)()
def my_decorator(some_function):
def wrapper():
print("Before we call the function.")
some_function()
print("After we call the function.")
return wrapper
# #my_decorator
def my_function(self):
print "My function is called here."
engine = MyClass()
engine.call('my_function')
The output is:
Executing function: my_function
My function is called here.
The decorator itself is identical to textbook examples. It looks like something goes wrong at a low level when calling a decorated method in Python with getattr.
Do you have any ideas on how to fix this code?
This has nothing to do with getattr(). You get the exact same error when you try to call my_function() directly:
>>> engine.my_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
You have 2 problems:
Your decorator never returns the wrapper, so None is returned instead. This return value replaces my_function and is the direct cause of your error; MyClass.my_function is set to None:
>>> MyClass.my_function is None
True
Your wrapper takes no arguments, including self. You'll need this for it to work once you do return it properly.
The first problem is fixed by un-indenting the return wrapper line; it is currently part of the wrapper function itself, and should be part of my_decorator instead:
def my_decorator(some_function):
def wrapper(self):
print("Before we call the function.")
# some_function is no longer bound, so pass in `self` explicitly
some_function(self)
print("After we call the function.")
# return the replacement function
return wrapper
Your question was only partially answered. Here's how to to modify the wrapper (as well as call()) methods so they accept additional arguments—that will make it work completely (as well as in both Python 2 and 3):
class MyClass:
def call(self, name, *args, **kwargs):
print("Executing function: {!r}".format(name))
getattr(self, name)(*args, **kwargs)
def my_decorator(some_function):
def wrapper(self, *args, **kwargs):
print("Before we call the function.")
retval = some_function(self, *args, **kwargs)
print("After we call the function.")
return retval
return wrapper
#my_decorator
def my_function(self):
print("My function is called here.")
del my_decorator # Not a permanent part of class.
engine = MyClass()
engine.call('my_function')
Output:
Executing function: 'my_function'
Before we call the function.
My function is called here.
After we call the function.

Categories