I have the following code:
run_in_background (
update_contacts(data={'email': email,'access_token': g.tokens['access_token']})
)
And I have created this function to process the called function in the background:
def run_in_background(function):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_in_executor(None, **function**, **data**)
How would I retrieve the function and the data that's being passed to it?
Use a decorator!
A decorator is a wrapper that takes a function as it's argument:
def decorate(func):
def wrapper(*args, **kwargs):
# *args and **kwargs are input vars passed to func() on its call
return f(*args, **kwargs)
return wrapper
#decorate
def func(arg1, arg2, **kwargs):
#do_things
In your case, this would look like:
def run_in_background(func):
def wrapper(*args, **kwargs):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# remember, data is in *args (or **kwargs if it's a keyword arg)
loop.run_in_executor(None, func, data)
return None
return wrapper
#run_in_background
# data is an arg or positional arg here, but could easily be data=data
def update_contents(data):
# function logic
Edit: Not always a background process:
You could change your decorator to switch between background and not:
def run_in_background(func):
def wrapper(*args, **kwargs):
if background:
# run in background
else:
func(data)
return None
return wrapper
#run_in_background
def update_contact(data, background=False):
#contact func logic
That will allow you to keep your decorator and reduce code duplication, while giving you the flexibility to specify if you want a background process or not
Related
Lately, I have started using decorators more extensively. For a project, I require different decorators which I built like in the schema below:
def param_check(fn=None, arg1=None, arg2=None):
def deco(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
#this is the only part that really changes
result = func(arg1, arg2)
return fn(result, *args, **kwargs)
return wrapper
if callable(fn): return deco(fn)
return deco
def func(arg1, arg2):
...do something...
.
.
...return something
However, since this is a lot of repeating code I was wondering what would be the most pythonic way to build a wrapper function that only takes in a function, and then returns a new decorator? I tried several ways but had no success. I was wondering if someone knows how to do that.
I think you just want to add another layer: the outermost layer will take an argument, and that's what gets called inside wrapper, not a hard-coded reference to func.
def decorator_maker(decfun):
def parameterized_decorator(fn=None, arg1=None, arg2=None):
def deco(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
#this is the only part that really changes
result = decfun(arg1, arg2)
return fn(result, *args, **kwargs)
return wrapper
if callable(fn): return deco(fn)
return deco
return parameterized_decorator
def func(arg1, arg2):
pass
param_check = decorator_maker(func)
# Or...
# #decorator_maker
# def func(arg1, arg2):
# ...
#param_check(3, 4)
def f(result):
...
I currently have a decorator that wraps a function into a class.
(We are currently using this weird, custom async framework where each async call is defined as a class with a ton of boilerplate code. My idea was to just decorate functions and then return the appropriate class.)
This decorator works fine on functions outside of classes. However, when using it with methods, the self argument is no longer implicitly passed, and I'm not sure why.
Here is the best example I could put together
from __future__ import print_function
import functools
def test_wrap(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print("Args:", args)
print("Kwargs:", kwargs)
func(*args, **kwargs)
return wrapper
def test_class_wrap(func):
"""Return a Command object for use with the custom framework we are using."""
#functools.wraps(func, assigned=('__name__', '__module__'), updated=())
class Command(object):
def __init__(self, *args, **kwargs):
print("Args:", args)
print("Kwargs:", kwargs)
func(*args, **kwargs)
return Command
class MyObject(object):
def __init__(self):
self.value = 100
#test_wrap
def foo(self):
print(self.value)
#test_class_wrap
def bar(self):
print(self.value)
if __name__ == '__main__':
obj = MyObject()
obj.foo()
print()
obj.bar(obj) # works
# obj.bar() # TypeError: bar() takes exactly 1 argument (0 given)
# Why is self implicitly passed as an argument like with outher methods?
# Output
# Args: (<__main__.MyObject object at 0x7fe2bf9bb590>,)
# Kwargs: {}
# 100
# Args: (<__main__.MyObject object at 0x7fe2bf9bb590>,)
# Kwargs: {}
# 100
test_class_wrap does nothing, just returning a class so __init__ isn't called. Try to wrap the class with a function passing args and kwargs:
def test_class_wrap(func):
"""Return a Command object for use with the custom framework we are using."""
#functools.wraps(func, assigned=('__name__', '__module__'), updated=())
def wrapper(*args, **kwargs):
class Command(object):
def __init__(self, *args, **kwargs):
print("Args:", args)
print("Kwargs:", kwargs)
func(*args, **kwargs)
return Command(*args, **kwargs)
return wrapper
...
if __name__ == '__main__':
obj = MyObject()
obj.foo()
print()
obj.bar()
In the below code, how would I obtain *args and **kwargs in function f without the need for the wrapper function?
def f(func):
def wrapper(*args, **kwargs):
print(args)
print(kwargs)
return func(*args,**kwargs)
return wrapper
#f
def write(text):
print(text)
# write = a(write)
write('dog')
Failed attempt 1:
def f(func):
a=func(*args)
k=func(**kwargs)
which causes error:
NameError: global name 'args' is not defined
Failed attempt 2:
def f(func(*args,**kwargs)):
a=func(*args)
k=func(**kwargs)
The wrapper function is necessary, and a standard part of how decorator definitions in Python work.
You can, however, help mask the existence of the wrapper function in tracebacks by using functools.wraps():
import functools
def f(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
This will update the wrapper function to have the name and docstring of the wrapped function.
--
Decorators are nothing more than functions which are passed a function. This code...
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
#dec
def myfunc(foo, bar):
return foo+bar
is equivalent to this code:
def dec(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def myfunc(foo, bar):
return foo+bar
myfunc = dec(myfunc)
Notice how the thing being passed to dec is a function which hasn't even been called yet - so there aren't any arguments passed at the time when dec is invoked. This is why the wrapper function is involved: it adds a layer which will be called when the original function is invoked which can capture arguments.
py.test seems to fail when I decorate test functions which has a fixture as an argument.
def deco(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
#pytest.fixture
def x():
return 0
#deco
def test_something(x):
assert x == 0
In this simple example, I get the following error:
TypeError: test_something() takes exactly 1 argument (0 given).
Is there a way to fix this, preferably without modifying the decorator too much? (Since the decorator is used outside testing code too.)
It looks like functools.wraps does not do the job well enough, so it breaks py.test's introspection.
Creating the decorator using the decorator package seems to do the trick.
import decorator
def deco(func):
def wrapper(func, *args, **kwargs):
return func(*args, **kwargs)
return decorator.decorator(wrapper, func)
Fixture feature depends on test function signature.
If you can change wrapper signature as follow, it will works.
def deco(func):
#functools.wraps(func)
def wrapper(x):
return func(x)
return wrapper
If you can't change it, make another decorator:
def deco(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def deco_x(func):
#functools.wraps(func)
def wrapper(x):
return func(x)
return wrapper
And decorate test_somthing with deco_x:
#deco_x
#deco
def test_something(x):
assert x == 0
Need to create a class that will do all things as the "merge" function. In class i will change, process and add new arguments.
def merge(*arg, **kwarg): # get decorator args & kwargs
def func(f):
def tmp(*args, **kwargs): # get function args & kwargs
kwargs.update(kwarg) # merge two dictionaries
return f(*args, **kwargs) # return merged data
return tmp
return func
Usage:
#other_decorator # return *args and **kwarg
#merge(list=['one','two','three']) # need to merge with #other_decorator
def test(*a, **k): # get merged args and kwargs
print 'args:', a
print 'kwargs:', k
I'm not sure I quite get what you're asking. Your implementation works fine, and you won't get around having two levels of indirection if you want to create a parametrized decorator of any kind.
To make merge a class you could do this
class Merge(object):
def __init__(self, **extra_kws):
self.extra_kws = extra_kws
def __call__(self, function):
def _wrapper(*args, **kws):
kws.update(self.extra_kws)
return function(*args, **kws)
return _wrapper
Then you can do this:
#Merge(foo='bar')
def test(*args, **kws):
print *args
print **kws
But you said you want to add change and process new arguments. So presumably you want the decorator itself to be live so you can do:
test.extra_kws['sun'] = 'dock'
After the decorator has been applied. In that case you probably don't want merge to be a class, but you want it to generate a class, so that test is replaced by the modifiable instance:
def merge(**extra_kws):
class _Merge(object):
def __init__(self, function):
self.extra_kws = extra_kws
self.function = function
def __call__(self, *args, **kws):
kws.update(self.extra_kws)
return self.function(*args, **kws)
return _Merge
#merge(foo='bar')
def test(*args, **kws):
print 'args:', args
print 'kws:', kws
test(sun='dock')
test.extra_kws['trog'] = 'cube'
test(sun='dock')
This then allows you to change the keywords on a particular decorated function later.
You could also do the same thing with function arguments without classes:
def merge(**extra_kws):
def _decorator(function):
def _wrapper(*args, **kws):
kws.update(_wrapper.extra_kws)
return function(*args, **kws)
_wrapper.extra_kws = extra_kws
return _wrapper
return _decorator
#merge(foo='bar')
def test(*args, **kws):
print 'kws:', kws
test(sun='dock')
test.extra_kws['trog'] = 'cube'
test(sun='dock')