Comments (4)
Not entirely sure what you are getting at and what the problem you are having is.
A few things to note about how wrapt is different and why.
The first thing is that the decorator wrapper function takes the arguments of the call as 'args' and 'kwargs'. This is instead of 'args' and '*kwargs', with possible variable substitution. In other words, it does not use:
@wrapt.decorator
def dec(wrapped, instance, *args, **kwargs):
print instance, args, kwargs
return wrapped(*args, **kwargs)
nor allow as a result:
@wrapt.decorator
def dec(wrapped, instance, bar, *args, **kwargs):
print instance, bar, args, kwargs
return wrapped(bar, *args, **kwargs)
@dec
def foo(bar, baz):
pass
This was a conscious decision because allowing this always raises the possibility of a decorated function using a argument name that could conflict with the arguments names used in the decorator wrapper function of 'wrapped', 'instance', 'args' and 'kwargs', as well as 'self' if the decorator wrapper function was actually a method of a class.
Especially where a decorator can be used in many contexts such as a timing decorator, to avoid problems, the person implementing the decorator would have to use magic argument names with special prefixes which they are pretty sure will not clash with the argument names of any function the decorator is applied to. Needing to adopt such a convention to me looks ugly and is a bit of a pain.
A further reason was that I found that early binding of the arguments could result in misleading error details about location. That is, the error would arise in applying the arguments to the decorator wrapper and not at the point where being applied to the wrapped function. In applying it against the decorator wrapper function, that the decorator wrapper had additional arguments would also confuse things because the error message would talk about a different number of arguments being passed to the function than what a user was expecting.
So that is some of the reasons why no early binding of arguments is done.
There are also other cases where late binding is good as well when one gets into using the wrappers for monkey patching and you need to to write an instrumentation module which works on multiple versions of a target package which has changed argument names and what arguments are accepted between versions. Having the unbound arguments passed as args and kwargs allows detection of such cases through trial binding against multiple prototypes within the decorator wrapper function if necessary.
In recent Python 3 version there is also inspect.getcallargs() which can help with such introspection.
>>> import inspect
>>> def foo(a, b, *args, **kwargs):
... pass
…
>>> inspect.getcallargs(foo, *(), **{'a':1, 'b':2})
{'a': 1, 'args': (), 'b': 2, 'kwargs': {}}
I am aware of how the decorator
module works, but I have various issues with that. This includes that it doesn't solve the universal decorator issue, that the inspect.getsource()
doesn't work from memory, plus the need to create a new compiled function with the correct signature rules it out for certain uses cases in doing monkey patching and instrumentation where the wrapper may need to be create dynamically on each call.
The runtime cost of compiling the new function on the fly is quite excessive in this latter case and was something that was going to note in my next blog post if I ever get to it where I was going to compare decorator
module performance to wrapt
module.
I don't know if this answers you question or solves your problem or whether you were just making an observation. If you can perhaps explain how the difference causes an issue then I can perhaps explain how you are meant to deal with it.
The documentation at:
already explains the need to explicitly bind the arguments in the decorator wrapper function if you need to use or change the arguments being passed in.
from wrapt.
Thanks for the reply. I guess it would have been easier to refer to the documentation that already mentions this issue, I just didn't read it carefully enough to see it being mentioned.
Specifically, it's this section:
You should not simply attempt to extract positional arguments from args directly because this will fail if those positional arguments were actually passed as keyword arguments, and so were passed in kwargs with args being an empty tuple.
There are two issues, I'd say:
- it's ugly to have to create an inner wrapper just to be able to get at the arguments. Were you able to rely on
args
beingargs
andkwargs
beingkwargs
it would have been much easier. - in one specific usage, one decorator I wrote accepts a
key_func
to create a cache key from the arguments passed in to the underlying function. That key function needs to match the exact argument names of the underlying function rather than being able to rely on argument order. This makes it harder/impossible to create such a key function that works with multiple underlying functions.
from wrapt.
Going to leave this issue open for now as reminder. I suspect what will help is to create a version of inspect.getcallargs() from Python 2.7 that works for Python 2.6 and expose it via a function in wrapt API.
Thus instead of doing something like:
@wrapt.decorator
def my_decorator(wrapped, instance, args, kwargs):
def _arguments(arg1, arg2, *args, **kwargs):
return (arg1, arg2)
arg1, arg2 = _arguments(*args, **kwargs)
# Do something with arg1 and arg2 but still pass through
# the original arguments to the wrapped function.
return wrapped(*args, **kwargs)
Could have something like:
@wrapt.decorator
def my_decorator(wrapped, instance, args, kwargs):
params = wrapt.getcallargs(wrapped, *args, **kwargs)
key_func = params['key_func']
…
return wrapped(*args, **kwargs)
The version of getcallargs() in Python is pure Python code, but I could always look at an optimised C version if made sense and sped things up.
from wrapt.
Have added wrapt.getcallargs() for version 1.7.0. For Python 2.7+ this is actually the same as inspect.getcallargs(), but for Python 2.6, it returns a local copy.
from wrapt.
Related Issues (20)
- Methods of proxied objects are bound to the original object, not the proxy HOT 4
- PySide6 behaves differently between decorated and non-decorated function HOT 16
- Idea: Sticky/Viral ObjectProxy HOT 3
- ObjectProxy does not play well with GenericAlias, such as isinstance(proxy, Dict) HOT 12
- RFE: is it possible to start making github releases?🤔 HOT 2
- Publish cp312 wheel HOT 6
- Release 1.14.1 Py3.11 wheels HOT 7
- __doc__ property HOT 9
- Request to be able to import both py and c wrappers HOT 8
- Unable to install wrapt 1.14.1 via poetry HOT 2
- Accessing ObjectProxy __dict__ HOT 5
- Update to setup.cfg potentially required HOT 4
- Documentation isn't building? HOT 6
- Best way to associate some data with `ObjectProxy`? HOT 3
- Accessing a class attribute that is a wrapt wrapped function will try and bind the function. HOT 4
- pydevd error when debugging with wrapt HOT 28
- Add type hint annotations for user-facing code HOT 2
- classmethod tests fail with Python 3.13 (Python reverted to pre-3.9 behavior) HOT 2
- 1.16.0: pytest fails
- How to use adapter factory to change signature depending on instance HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from wrapt.