Obtaining *args and **kwargs from passed func - python

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.

Related

How to get function name, function params

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

Conditional Decorator Implementation

I want to have conditional decorator. Following is my code:-
def int_decorator(func): # checks whether the args passed is of type int.
def wrapper(*args, **kwargs):
a, b = args
if not (isinstance(a, int) and isinstance(b, int)):
return
return func(*args, **kwargs)
return wrapper
decorator_mapping = {
'int': int_decorator
}
class conditional_decorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
decorator = decorator_mapping.get(kwargs.get('decorator'))
if not decorator:
# Return the function unchanged, not decorated.
return self.func(*args, **kwargs)
return decorator(self.func(*args, **kwargs))
#conditional_decorator
def func(a, b, *args, **kwargs):
return(1)
print(func(1, 2, decorator='int'))
What I want is if there exist any decorator in decorator_mapping that matches the value passed in the func, then apply the same decorator on the function, else don't apply any decorator.
The above code works well, when there isn;t any decorator, but fails when a decorator is found. It print's the reference of the function.
Where am I wrong?
Decorator accepts a function, returns a function. This line:
return decorator(self.func(*args, **kwargs))
should be:
return decorator(self.func)(*args, **kwargs)
def __call__(self, *args, **kwargs):
decorator = decorator_mapping.get(kwargs.get('decorator'))
if not decorator:
# Return the function unchanged, not decorated.
return self.func(*args, **kwargs)
return decorator(self.func)(*args, **kwargs)
You want to decorate the function, then call the decorated version of it.

how to pass decorated function arguments in decorator

I have code below
#newrelic.agent.data_store_trace('Mysql', '<name>',None)
def get_user(request=None, name=settings.DEFAULT_NAME):
# Some implementation
In the decorator, in place of <name> I want to pass name which is in the decorated function.
Note that I don't want to modify/override the decorator as newrelic updates the packages time-to-time, It would be a problem for us.
Any solution???
Decorators are simply wrapper functions.
Write another wrapper function that wraps newrelic.agent.data_store_trace and allows to pass a name.
Say New Relic defines data_store_trace as:
import functools
def data_store_trace(product, target, operation):
def wraps(fn):
#functools.wraps(fn)
def wrapped(*args, **kwargs):
print('Tracing: ', fn.__name__, args, kwargs)
return fn(*args, **kwargs)
return wrapped
return wraps
Adding another level of indirection will give:
def data_store_trace_with_name(product, operation, target=''):
def wraps(fn):
#functools.wraps(fn)
def wrapped(*args, **kwargs):
return data_store_trace(product, target, operation)(fn)(*args, **kwargs)
return wrapped
return wraps
#data_store_trace_with_name('Mysql', None, '<name>')
def add(x, y):
return x + y
add(5, 6)

Using a decorator that returns a class on a method?

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()

How do I make pytest fixtures work with decorated functions?

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

Categories