Decorator not work with argument suggestions? - python

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.

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)

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)

Python decorator to keep signature and user defined attribute

I have my simple decorator my_decorator which decorates the my_func.
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = 'my_decorator'
return wrapper
#my_decorator
def my_func(x):
print('hello %s'%x)
my_func._decorator_name_
'my_decorator'
Till here things work, but I can't see the actual signature of the function.
my_func?
Signature: my_func(*args, **kwargs)
Docstring: <no docstring>
File: ~/<ipython-input-2-e4c91999ef66>
Type: function
If I decorate my decorator with python's decorator.decorator, I can see the signature of my function but I can't have the new property which I have defined.
import decorator
#decorator.decorator
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = 'my_decorator'
return wrapper
#my_decorator
def my_func(x):
print('hello %s'%x)
my_func?
Signature: my_func(x)
Docstring: <no docstring>
File: ~/<ipython-input-8-934f46134434>
Type: function
my_func._decorator_name_
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-10-7e3ef4ebfc8b> in <module>()
----> 1 my_func._decorator_name_
AttributeError: 'function' object has no attribute '_decorator_name_'
How can I have both in python2.7?
For Python 3, using functools.wraps in standard library:
from functools import wraps
def my_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = 'my_decorator'
return wrapper
#my_decorator
def my_func(x):
print('hello %s'%x)
print(my_func._decorator_name_)
#decorator.decorator returns a function which takes another function as input. In your case you want the attribute on the returned function.
For it to work on Python 2.7, you just need some tweak
import decorator
def my_dec2(func):
#decorator.decorator
def my_decorator(func, *args, **kwargs):
print("this was called")
return func(*args, **kwargs)
test = my_decorator(func)
test._decorator_name_ = "my_decorator"
return test
#my_dec2
def my_func(x):
print('hello %s'%x)
my_func(2)
print(my_func._decorator_name_)
And then when you test it works
In [1]: my_func?
Signature: my_func(x)
Docstring: <no docstring>
File: ~/Desktop/payu/projects/decotest/decos.py
Type: function
In [2]: my_func._decorator_name_
Out[2]: 'my_decorator'
You only need to define a wrapper function if you somehow want to alterate the behaviour of your function. So in the case you really just want to add some attribute to your function without changing its behaviour, you can simply do the following.
def my_decorator(func):
func._decorator_name_ = 'my_decorator'
return func
In the more complex case where you need to have a wrapper, what I suggest is following the accepted answer for this question which explains how to create a function that behaves the same as another, but has a customised signature. Using inspect.getargspec you can recover the signature from my_func and transpose it to your wrapper.
As others have pointed out, you do not seem to use decorator correctly.
Alternately you can use my library makefun to create your signature-preserving wrapper, it relies on the same trick than decorator to preserve signature, but is more focused on dynamic function creation and is more generic (you can change the signatures):
from makefun import wraps
def my_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = 'my_decorator'
return wrapper
You can check that it works as expected:
#my_decorator
def my_func(x):
"""my function"""
print('hello %s' % x)
assert my_func._decorator_name_ == 'my_decorator'
help(my_func)
For what it's worth, if you wish to later add optional arguments to your decorator without making the code look more complex, have a look at decopatch. For example if you want _decorator_name_ to be an optional argument of the decorator:
from decopatch import function_decorator, DECORATED
from makefun import wraps
#function_decorator
def my_decorator(name='my_decorator', func=DECORATED):
#wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
wrapper._decorator_name_ = name
return wrapper

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.

Mock python decorator in unit tests

I am trying to test a function that is decorated. Is there a way to mock a decorator and test function in isolation, when decorator is already applied?
import mock
def decorator(func):
def wrapper(*args, **kwargs):
return 1
return wrapper
def mocked(func):
def wrapper(*args, **kwargs):
return 2
return wrapper
#decorator
def f():
return 0
with mock.patch('test.decorator') as d:
d.side_effect = mocked
assert f() == 2 # error
There is not a simple solution.
This is a similar question: How to strip decorators from a function in python
You can either modify the original code just for testing, or use something like this library: https://pypi.python.org/pypi/undecorated in order to write a helper function to switch from the original wrapper to the testing wrapper:
from undecorated import undecorated
mocked(undecorated(f))()

Categories