Giter VIP home page Giter VIP logo

wrapt's Introduction

wrapt

Documentation Status PyPI

The aim of the wrapt module is to provide a transparent object proxy for Python, which can be used as the basis for the construction of function wrappers and decorator functions.

The wrapt module focuses very much on correctness. It therefore goes way beyond existing mechanisms such as functools.wraps() to ensure that decorators preserve introspectability, signatures, type checking abilities etc. The decorators that can be constructed using this module will work in far more scenarios than typical decorators and provide more predictable and consistent behaviour.

To ensure that the overhead is as minimal as possible, a C extension module is used for performance critical components. An automatic fallback to a pure Python implementation is also provided where a target system does not have a compiler to allow the C extension to be compiled.

Documentation

For further information on the wrapt module see:

Quick Start

To implement your decorator you need to first define a wrapper function. This will be called each time a decorated function is called. The wrapper function needs to take four positional arguments:

  • wrapped - The wrapped function which in turns needs to be called by your wrapper function.
  • instance - The object to which the wrapped function was bound when it was called.
  • args - The list of positional arguments supplied when the decorated function was called.
  • kwargs - The dictionary of keyword arguments supplied when the decorated function was called.

The wrapper function would do whatever it needs to, but would usually in turn call the wrapped function that is passed in via the wrapped argument.

The decorator @wrapt.decorator then needs to be applied to the wrapper function to convert it into a decorator which can in turn be applied to other functions.

import wrapt

@wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)

@pass_through
def function():
    pass

If you wish to implement a decorator which accepts arguments, then wrap the definition of the decorator in a function closure. Any arguments supplied to the outer function when the decorator is applied, will be available to the inner wrapper when the wrapped function is called.

import wrapt

def with_arguments(myarg1, myarg2):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)
    return wrapper

@with_arguments(1, 2)
def function():
    pass

When applied to a normal function or static method, the wrapper function when called will be passed None as the instance argument.

When applied to an instance method, the wrapper function when called will be passed the instance of the class the method is being called on as the instance argument. This will be the case even when the instance method was called explicitly via the class and the instance passed as the first argument. That is, the instance will never be passed as part of args.

When applied to a class method, the wrapper function when called will be passed the class type as the instance argument.

When applied to a class, the wrapper function when called will be passed None as the instance argument. The wrapped argument in this case will be the class.

The above rules can be summarised with the following example.

import inspect

@wrapt.decorator
def universal(wrapped, instance, args, kwargs):
    if instance is None:
        if inspect.isclass(wrapped):
            # Decorator was applied to a class.
            return wrapped(*args, **kwargs)
        else:
            # Decorator was applied to a function or staticmethod.
            return wrapped(*args, **kwargs)
    else:
        if inspect.isclass(instance):
            # Decorator was applied to a classmethod.
            return wrapped(*args, **kwargs)
        else:
            # Decorator was applied to an instancemethod.
            return wrapped(*args, **kwargs)

Using these checks it is therefore possible to create a universal decorator that can be applied in all situations. It is no longer necessary to create different variants of decorators for normal functions and instance methods, or use additional wrappers to convert a function decorator into one that will work for instance methods.

In all cases, the wrapped function passed to the wrapper function is called in the same way, with args and kwargs being passed. The instance argument doesn't need to be used in calling the wrapped function.

Repository

Full source code for the wrapt module, including documentation files and unit tests, can be obtained from github.

wrapt's People

Contributors

a-feld avatar adamchainz avatar agriffis avatar althonos avatar bnavigator avatar candeira avatar daniel-o-jones avatar eire1130 avatar eltoder avatar federicostra avatar ghostkeeper avatar grahamdumpleton avatar gst avatar hongweipeng avatar hugovk avatar ionelmc avatar jouve avatar jwilk avatar odidev avatar rsyring avatar stegayet avatar thorwhalen avatar timgates42 avatar varunagrawal avatar yarikoptic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wrapt's Issues

Dynamically changing 'instance' in a wrapt decorator on an instance method

Imagine we have the following class:

class Image(object):

    def __init__(self):
        self.pixels = np.ones((100, 100))

    def crop(self):
        self.pixels = self.pixels[:40, :40]

which behaves as:

img = Image()
assert img.pixels.shape = (100, 100)
x = img.crop()
assert x == None
assert img.pixels.shape = (40, 40)

it would be desirable to have some magic decorator @inplace that modifies the behaviour as follows:

class Image(object):

    def __init__(self):
        self.pixels = np.ones((100, 100))

    @inplace
    def crop(self):
        self.pixels = self.pixels[:40, :40]

which behaves as:

img = Image()
assert img.pixels.shape = (100, 100)
x = img.crop()
assert type(x) == Image  # decorator made a copy
assert img.pixels.shape = (100, 100)  # original image untouched
assert x.pixels.shape = (40, 40)  # returned image was modified

y = img.crop(inplace=True)  # kwarg instructs the decorator to not interfere
assert y == None 
assert img.pixels.shape = (40, 40)  # original 'inplace' behavior

In our project we have many situations like this where a copy of the object in question may be expensive (so in performance-critical internal API you would like to modify objects in place) but for interactive exploratory work we would prefer the safely and clarity of (in effect) immutable objects.

We have a unified approach to copying objects in our framework, so in psuedocode we could do something like

@wrapt.decorator
def inplace(wrapped, instance, args, kwargs):
    def _execute(*args, **kwargs):
        inplace = kwargs.pop('inplace', False)
        if inplace:
            # just call the method (which is always written to modify self)
            wrapped(*args, **kwargs) 
            return None  # prevent accidental chaining when inplace
        else:
            new_instance = instance.copy()  # assume we know instance has .copy() 
            # call the self-modifying method on a new self
            new_instance.wrapped(*args, **kwargs)
            return new_instance  # instance left unmodified
    return _execute(*args, **kwargs)

My questions is - can something like this be done in wrapt? I see in the docs that you say:

When calling the wrapped function in the decorator wrapper function, the instance should never be passed explicitly though. This is because the instance is already bound to wrapped and will be passed automatically as the first argument to the original wrapped function.

Which makes me think I am basically asking how I can circumvent that and change the instance that is passed by wrapt dynamically at the invocation of the decorated function.

Best Wishes,
James

p.s. our project is using wrapt to define functions that transparently work on numpy arrays or our own datatypes, and we are really grateful for the work you have put in to developing such a solid approach to decorators.

Missing init checks in wrapper.c cause segfault.

Code to repro:

Python 2.7.3 (default, Sep 26 2013, 20:03:06)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import wrapt
>>> wrapt.wrappers.ObjectProxy.__new__(wrapt.wrappers.ObjectProxy) + wrapt.wrappers.ObjectProxy.__new__(wrapt.wrappers.ObjectProxy)
[1]    25370 segmentation fault (core dumped)  .ve/bin/python

Improve efficiency of string comparisons in WraptObjectProxy_setattro().

When checking for 'self' prefix, currently invoking starts with() method of the string object.

It would be more efficient where the name is a string object to use PyString_AsString() to get access to the character array and then use strstr() or equivalent.

Similarly for a Unicode string and using Python 3, can use PyUnicode_AsUTF8AndSize().

Failure if combined with non-wrapt decorators

When attempting to solve problems with decorating both methods/functions in Django, I came up with a short suite of tests which included real-life tricky things that people wanted to do with functions/methods/decorators that we provided. We did have an auto_adapt_to_methods function but came across corner cases we couldn't fix, so we ended up with the manual method_decorator function which has to be applied manually to make method decorators out of function decorators.

So I was intrigued to know if you've solved the problems we hit! Below is the script, adapted to implement our decorators using wrapt. You'll be glad to know if passes most of them, but it fails on the last test, where there is a simple decorator which, by definition, doesn't use our solution to these problems. (For a framework like Django, it's important that we use things that will play nice with normal Python).

If this is a known issue, then I guess it should be in the "Known Issues" section of the docs.

Thanks!

#!/usr/bin/env python

import wrapt

@wrapt.decorator
def striparg(wrapped, instance, args, kwargs):
    """
    Decorates functions that take a single string argument, and runs
    strip() on that arg before calling.
    """
    return wrapped(args[0].strip())


@wrapt.decorator
def sign(wrapped, instance, args, kwargs):
    """
    Decorates *methods* that take a single argument and return a string.
    """
    return wrapped(args[0]) + " (says " + instance.__class__.__name__ + ")"


def simple_decorator(func):
    """
    No-op decorator, implemented by someone else
    """
    def wrapper(*args):
        return func(*args)
    return wrapper


@striparg
def insult(arg):
    return arg + " sucks!"


class Stuff(object):
    @striparg
    def deny(self, arg):
        return "No you can't have " + arg


class MoreStuff(Stuff):
    deny = Stuff.deny
    deny2 = simple_decorator(Stuff.deny)
    deny3 = sign(Stuff.deny)
    deny4 = striparg(Stuff.deny)

    @simple_decorator
    def deny5(self, arg):
        return super(MoreStuff, self).deny(arg)

    @sign
    def deny6(self, arg):
        return super(MoreStuff, self).deny(arg)

    @simple_decorator
    @striparg
    def deny7(self, arg):
        return super(MoreStuff, self).deny(arg)


assert insult(" PHP ") == "PHP sucks!"
assert Stuff().deny(" a pony ") == "No you can't have a pony"
assert MoreStuff().deny(" a backwards incompatible change ") == "No you can't have a backwards incompatible change"
assert MoreStuff().deny2(" some figgy pudding ") == "No you can't have some figgy pudding"
assert MoreStuff().deny3(" a velociraptor ") == "No you can't have a velociraptor (says MoreStuff)"
assert MoreStuff().deny4(" true freedom on your iPad ") == "No you can't have true freedom on your iPad"
assert MoreStuff().deny5(" a design contest ") == "No you can't have a design contest"
assert MoreStuff().deny6(" less politically charged example code ") == "No you can't have less politically charged example code (says MoreStuff)"
assert MoreStuff().deny7(" an auto_adapt_to_methods function that works ") == "No you can't have an auto_adapt_to_methods function that works" # fails

RFE: Changing unknown signatures

Hi,

you already provide the means to change the signature of a function as documented on http://wrapt.readthedocs.org/en/latest/decorators.html#signature-changing-decorators .

However in our project we have the need of changing the signature of an arbitrary function. Example:

@decorated
def some_function(arg, arg3: annotation):
    pass  # It also may do something :)

@wrapt.decorator(remove_args=["arg"], add_args_before=["arg1", "arg2"])
def decorated(wrapped, instance, args, kwargs):
    return wrapped(args[0]*args[1], *(args[2:]), **kwargs)

So, we want a decorator that replaces specific arguments in an unknown signature while leaving the function introspectable (including annotations especially). Would you be interested in having this in your library? Otherwise we'll have to do a nongeneralized version for ourselves only.

IndexError when calling wrapped function from the class

from wrapt import decorator

@decorator
def simple_logger(*arg):
    print 'prefix', arg

class A(object):

    @simple_logger
    def boo(self, *arg):
        print arg

A.boo()

that will result in:

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    A.boo()
  File "/local/lib/python2.7/site-packages/wrapt/wrappers.py", line 485, in __call__
    instance, args = args[0], args[1:]
IndexError: tuple index out of range

with the C extension installed it's:

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    A.boo()
IndexError: tuple index out of range

WeakFunctionProxy bypasses wrapt.decorator

Thank you for putting in the effort to write this package and explaining all the issues it addresses. My decorators would be much more fragile without your help!

WeakFunctionProxy only proxies the original function, and ignores any patching done via wrapt.decorator. Ex (tested in python 2.7 with wrapt 1.10.4):

import wrapt

@wrapt.decorator
def bark(wrapped, instance, args, kwargs):
    print 'The %s barks' % instance.__class__.__name__

class Animal(object):
    @bark
    def squeal(self):
        print 'The %s squeals' % self.__class__.__name__

animal = Animal()
animal.squeal()

Output: The Animal barks

weak_squeal = wrapt.WeakFunctionProxy(animal.squeal)
weak_squeal()

Output: The Animal squeals

Argument passing to decorator doesn't match "native" behaviour

The argument passing to a decorator is "different" from how an undecorated function works, in that parameters defined as regular arguments gets put into "kwargs" if passed by name to the decorated function:

import inspect
import wrapt

@wrapt.decorator
def dec(wrapped, instance, args, kwargs):
    print instance, args, kwargs
    return wrapped(*args, **kwargs)

@dec
def foo(bar, baz):
    pass

foo(1,2)
    -> None (1, 2) {}
foo(baz=1,bar=2)
    -> None () {'baz': 1, 'bar': 2}

inspect.getargspec(foo)
    -> ArgSpec(args=['bar', 'baz'], varargs=None, keywords=None, defaults=None)

This is mainly an issue for argument introspection in the decorator, and the "workaround" to have an inner wrapper is unwieldy when it works, and doesn't work at all for some uses.

The decorator package solves this issue by creating a new function with the correct signature, but I'd like it a lot if I could standardize on the wrapt package.

Document the discussion in #33

When I went to try to figure out what the purpose of the adapter argument to wrapt.decorator, which I only found by reading the code, I eventually ended up reading through the closed issue because it wasn't otherwise documented. A brief summary, possibly with some of the examples used in the issue, would be useful somewhere in the docs, maybe under an "Advanced Usage" or similar heading?

Performing computations after the decorator is applied to the function but before the decorated function is called

I'm trying to mimic Python 3's keyword-only arguments in Python 2 with a decorator. The general pattern is,

def decorator_factory(kwonly_args):
    def decorator(function):
        inspect.getargspec(function)
        # do some calculations based on the argspec
        def wrapper(*args, **kws):
            # do more stuff with the previous results and args, kws
            return function(*new_args, **new_kws)
        return wrapper
    return decorator

The full code is at https://code.activestate.com/recipes/578993-keyword-only-arguments-in-python-2x/.

With wrapt.decorator the receiving function gets the wrapped function and its arguments at the same time, which would mean redoing the work with inspect.getargspec and its sequents with every call. If there's a way to prevent that from happening, I don't see it. Is there a way I'm not seeing? If not, would you. consider changing the API to make it possible?

Support the dynamic generation of signatures for signature changing decorators

wrapt currently supports signature changing decorators with the keyword argument adapter which accepts a callable at the time that wrapt.decorator is invoked (i.e. when the decorator function is decorated). This means that the signature of functions produced by any given decorator built with wrapt.decorator is fixed.

It is easy to imagine a use case for a decorator that operates on functions with arbitrary signatures, and modifies their signatures in a predictable way. Consider, for example, a decorator function that skips a test function based on the value of a command line flag that was passed to pytest. In pytest, the way one obtains access to command line flags is through the request fixture, which is typically obtained with the addition of an argument to the test functions signature. As such, a decorator implementing this behavior would need to add an argument to the test functions signature. Because any given test function may request any number of other fixtures, the decorator function can not assume some fixed signature as in:

@wrapt.decorator(adapter=lambda request: None)
def skip_if_flag(function, instance, args, kwargs):
    request = kwargs.pop('request')
    if request.config.getoption('flag_name'):
        pass
    else:
        return function(*args, **kwargs)

More exactly, such a decorator would only work with test functions that don't request any fixtures.

It is actually possible to implement the more general version of this decorator with wrapt, but the code that does so borders on hideous:

class FunctionWithSignatureBuilder(object):

    NO_ARGUMENT = object()

    def __init__(self, function, *arguments_to_add, **keyword_arguments_to_add):
        self._function = function
        self._arguments_to_add = arguments_to_add
        self._keyword_arguments_to_add = keyword_arguments_to_add
        self._argspec = inspect.getargspec(function)
        defaults = self._argspec.defaults or ()
        positional_arg_count = (len(self._argspec.args) -
                                len(defaults))
        self._positional_pairs = zip(
            list(arguments_to_add) + self._argspec.args[:positional_arg_count],
            [self.NO_ARGUMENT] * (positional_arg_count + len(arguments_to_add))
        )
        self._keyword_pairs = zip(
            self._argspec.args[positional_arg_count:],
            defaults
        ) + keyword_arguments_to_add.items()
        self._argument_pairs = self._positional_pairs + self._keyword_pairs
        if self._argspec.varargs:
            self._argument_pairs.append(
                ('*{0}'.format(self._argspec.varargs), self.NO_ARGUMENT)
            )
        if self._argspec.keywords:
            self._argument_pairs.append(
                ('**{0}'.format(self._argspec.keywords), self.NO_ARGUMENT)
             )

    @property
    def argument_list_string(self):
        return ', '.join(self._build_argument_string(argument_pair)
                         for argument_pair in self._argument_pairs)

    @property
    def null_function_string(self):
        return 'lambda {0}: None'.format(self.argument_list_string)

    @property
    def null_function(self):
        return eval(self.null_function_string)

    def _build_argument_string(self, incoming):
        argument_name, default = incoming
        if default == self.NO_ARGUMENT: return argument_name
        return '{0}={1}'.format(argument_name, repr(default))


def skip_if_flag(function):
    @decorator(
        adapter=FunctionWithSignatureBuilder(function, 'request').null_function
    )
    def wrapped(function, instance, args, kwargs):
        request = kwargs.pop('request')
        if request.config.getoption('flag_name'):
        pass
        else:
            return function(*args, **kwargs)
    return wrapped(function)

This example highlights two separate issues with wrapts current adapter based interface:

a) It is not possible to create decorators that operate on functions with arbitrary signatures.

b) It is more difficult than it should be to provide a modify the signature of a function in a fixed way.

To address the first of these issues I propose the addition of a keyword argument to the wrapt.decorator function that accepts a function that produces the output signature of the decorator given the input function. This is how you would write the decorator from my example using this new keyword:

@wrapt.decorator(signature_builder=lambda function: FunctionWithSignatureBuilder(function, 'request').null_function)
def skip_if_flag(function, instance, args, kwargs):
    request = kwargs.pop('request')
    if request.config.getoption('flag_name'):
        pass
    else:
        return function(*args, **kwargs)

I see two viable solutions to b). The first of these is to simply include a class/function like FunctionWithSignatureBuilder in the wrapt library (it would need to support argument erasure as well as a few other things to really be a viable solution). The other option would be to change the interface to adapter/signature_builder so that they accept some type of custom object that contains argument information (or perhaps ArgSpec instances [The type returned from inspect.getargspec]) rather than function instances. I prefer the second option since it avoids adding code that eval's things to the codebase (by the way, do you know of a better way to do that), but the second option is definitely more disruptive. Perhaps the best thing to do would be to only change the expected return type of signature_builder while leaving adapter as it is.

Anyway, I'm happy to implement this functionality, but I wanted to hear your thoughts before I started working on anything.

Decorating a subclass fails

I might be doing something wrong but in both Python 2.7.5 and Python 3.3.4, the following code fails.

import wrapt

def with_arguments( value ):
    @wrapt.decorator
    def wrapper( wrapped, instance, args, kwargs ):
        return wrapped( *args, **kwargs )
    return wrapper

class Ancestor( object ):
    @with_arguments( value='myValue' )
    class Child( object ):
        pass

print( Ancestor.Child )

The traceback is as follows:

Traceback (most recent call last):
  File "/Users/christopher/Desktop/decorator/test.py", line 15, in <module>
    print( Ancestor.Child )
  File "/Users/christopher/Desktop/decorator/wrapt/wrappers.py", line 428, in __get__
    descriptor = self.__wrapped__.__get__(instance, owner)
AttributeError: type object 'Child' has no attribute '__get__'

As a temporary workaround I've wrapped the content of the _FunctionWrapperBase::__get__() method into a try/except statement that returns self when an exception is raised. Sounds quite hacky though! :)

Better error message when transient_function_wrapper given bad target object to patch.

def transient_function_wrapper(module, name):
    def _decorator(wrapper):
        @function_wrapper
        def _wrapper(wrapped, instance, args, kwargs):
            try:
                (parent, attribute, original) = resolve_path(module, name)
                replacement = FunctionWrapper(original, wrapper)
                setattr(parent, attribute, replacement)
                return wrapped(*args, **kwargs)
            finally:
                setattr(parent, attribute, original)
        return _wrapper
    return _decorator

If resolve_path() fails parent will not be set, yet in the finally clause parent is used giving:

UnboundLocalError: local variable 'parent' referenced before assignment

Not obvious what original problem was as original exception masked.

inspect.signature() on 3.2

Can you add fallback for 3.2?

$ pypy3
Python 3.2.5 (986752d005bb6c65ce418113e4c3cd115f61a9b4, Jul 08 2014, 12:36:44)
[PyPy 2.3.1 with GCC 4.9.0 20140604 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>> import vcr
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/mdevaev/.local/lib/python3.2/site-packages/vcr/__init__.py", line 2, in <module>
    from .config import VCR
  File "/home/mdevaev/.local/lib/python3.2/site-packages/vcr/config.py", line 6, in <module>
    from .cassette import Cassette
  File "/home/mdevaev/.local/lib/python3.2/site-packages/vcr/cassette.py", line 5, in <module>
    import wrapt
  File "/home/mdevaev/.local/lib/python3.2/site-packages/wrapt/__init__.py", line 10, in <module>
    from .decorators import decorator, synchronized
  File "/home/mdevaev/.local/lib/python3.2/site-packages/wrapt/decorators.py", line 17, in <module>
    from inspect import signature
ImportError: cannot import name 'signature'

Deriving from ObjectProxy (is it possible?)

Hi, I'm trying to use ObjectProxy in my code to replace a piece of code that I have that is very similar:

The code is @ https://github.com/openstack/taskflow/blob/master/taskflow/utils/deprecation.py#L33

I am trying to have the MovedClassProxy derive from your ObjectProxy so that I can take advantage of the correctness of the ObjectProxy base class (and still retain the ability to emit depreciation warnings like that MovedClassProxy does). Unfortunately when I try to do this it appears I am getting the following warning/error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "taskflow/utils/misc.py", line 395, in <module>
    version="0.5", removal_version="?")
  File "taskflow/utils/deprecation.py", line 116, in moved_class
    return MovedClassProxy(new_class, "".join(message_components), 3)
  File "taskflow/utils/deprecation.py", line 59, in __init__
    _setattr(self, '__message__', message)
TypeError: can't apply this __setattr__ to ObjectProxy object

This appears to be due to https://github.com/openstack/taskflow/blob/master/taskflow/utils/deprecation.py#L58 which is needed to retain the depreciation message. Any ideas how this can be done correctly using ObjectProxy as a parent class (or maybe there is another way?).

missing properties on decorated class methods

given

import wrapt

def cached():
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return hasattr(wrapped, 'im_class')
    return wrapper

class Classo(object):
    @classmethod
    @cached()
    def methodc(cls): 
        pass

    @staticmethod
    @cached()
    def methods():
        pass

    @cached()
    def method(self): 
        pass

@cached()
def methodf(hello, there):
    pass

the following results

print(hasattr(Classo.method, 'im_class'))    # True
print(hasattr(Classo.methodc, 'im_class'))  # True
print(hasattr(Classo.methods, 'im_class'))  # False
print(hasattr(methodf, 'im_class'))  # False

print(Classo().method())  # True
print(Classo.methodc())   # False / I'd hoped it would be True
print(Classo.methods())   # False
print(methodf())  # False

transient_function_wrapper can't be used on class methods

Cant do something like:

class transient_with_arguments(object):

    def __init__(self, myarg1, myarg2):
        self.myarg1 = myarg1
        self.myarg2 = myarg2

    @wrapt.transient_function_wrapper('module', 'object')
    def __call__(self, wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)

@transient_with_arguments(1, 2)
def function():
    pass

Needs to be made aware of when being applied to class methods as was done for decorator/function_wrapper.

@synchronized doesn't seem to be working

I downloaded the package for the @synchronized decorator. This test suggests it doesn't work:

from wrapt.decorators import synchronized
import threading
import time
import sys
import logging
from threading import Thread, RLock

log = lambda x: sys.stdout.write("%s\n" % x)


class TestThread(Thread):

    @synchronized
    def crit(self):
        #with self.lock:
            log("%s before sleep" % self.name)
            time.sleep(1)
            log("%s after sleep" % self.name)

    def run(self):
        log("%s before calling" % self.name)
        self.crit()


a = TestThread()
a.name = "A"
b = TestThread()
b.name = "B"

a.start()
b.start()

Running this program finishes in 1 second and sleep methods run concurrently, whereas I'd expect them to run sequentially.

Make ObjectProxy Picklable

Python has some problems pickling wrappers in general, described here. Below is a test case:

>>> from wrapt import ObjectProxy
>>> import pickle
>>> pickle.dumps(ObjectProxy(1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: args[0] from __newobj__ args has the wrong class

But is there a way to at least make ObjectProxy picklable? __reduce__ works but requires a list of attributes, I'm not sure how to get that automatically. Keeping them in __setattr__ can introduce lots of overhead.

class PicklableProxy(ObjectProxy):
    def __init__(self, wrapped, attrs):
        super().__init__(wrapped)
        self._self_attrs = attrs

    def __reduce__(self):
        return type(self), (self.__wrapped__, self._self_attrs), tuple(getattr(self, attr) for attr in self._self_attrs)

    def __setstate__(self, state):
        for attr, value in zip(self._self_attrs, state):
            setattr(self, attr, value)

Subclassing proxied classes fails

In python 3.4 (and probably other versions, just didn't test against them), if you try the following:

@wrapt.CallableObjectProxy
class Foo:
    pass
class Foo(Bar):
    pass

You will get a TypeError complaining about the number of arguments you are passing to the ObjectProxy constructor (at least with the c-extension version).

I tried fiddling with this a little, but I couldn't really think of any way to fix this. At the very least, if this is not fixable, this behavior should be documented.

Require explicit decorator factories where appropriate

Having a real new-style proxy type available is very cool, but the current API design and docs perpetuate a terminology problem we've been trying to fix in the core docs for years. Decorators are callables that accept a callable as their sole positional argument, and return an appropriate result derived from that callable. A callable that accepts arbitrary parameters and returns a decorator is a decorator factory, and not itself a decorator.

The consequence of blurring the lines is clear in this paragraph in the current docs:

"Note that even if all your decorator arguments have default values, or you only have a variable arguments list or keyword argument dictionary, you must still provide the parantheses to the decorator when used. Once you opt to have the decorator be able to accept arguments the use of the parentheses is not optional."

This paragraph shows that wrapt.decorator is implementing two different concepts: it declares a decorator, or a decorator factory, but you have to look at the signature definition of the wrapped object to decide which it is. That's dubious-but-tolerable if you use it solely as a decorator expression adjacent to the signature of the wrapped function, but completely unworkable if used as a dynamic wrapper - you don't know if the result will be a decorator or a decorator factory, because it depends on the signature of the supplied callable.

Rather than have the behaviour magically change based on whether the wrapped callable accepts additional argument beyond the required four, why not require people to write ordinary closures:

import wrapt

def with_arguments(myarg1, myarg2):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)
    return wrapper

@with_arguments(1, 2)
def function():
    pass

The other advantage of this approach is that any derived values of the factory arguments can be calculated once in the closure, rather than needing to be recalculated on each iteration.

Best way to modify wrapped __doc__

I've been working on a debtcollector library:

http://docs.openstack.org/developer/debtcollector/

It uses wrapt now and we are currently adding the ability to automatically adjust the docstring of depreciated methods/functions... with documentation that states what is deprecated. This has turned out to be pretty easy for decorators that aren't using wrapt but I was wondering the best way to do this for decorators that are using wrapt.

I came up with http://paste.openstack.org/show/176557/ but was wondering if there is a better way to do this when a object being decorated is using the wrapt decorator mechanism. Any thoughts would be appreciated.

Btw the review for this kind of stuff is @ https://review.openstack.org/#/c/155988/

Add wrapper for overriding instance attribute access.

Add wrapper for overriding instance attribute access.

This would be used where it is necessary to wrap the object returned when an attribute of an object is accessed. It would work by adding a data descriptor on the class corresponding to the attribute that would exist on the instance.

unable to return different functions with non argument style decorator

this works

import wrapt

def cach():
    pass

def cached():
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return cach()
    return wrapper

@cached()
def function():
    pass

this does not

import wrapt

def cach():
    pass

@wrapt.decorator
def cached(wrapped, instance, args, kwargs):
    return cach()

@cached()
def function():
    pass

pip install -e from git doesn't work

$ pip install -e git://github.com/grahamdumpleton/wrapt.git#egg=wrapt-dev
Obtaining wrapt from git+git://github.com/grahamdumpleton/wrapt.git#egg=wrapt-dev
  Cloning git://github.com/grahamdumpleton/wrapt.git to /home/vagrant/.virtualenvs/pp/src/wrapt
Installing collected packages: wrapt
  Running setup.py develop for wrapt
Successfully installed wrapt

$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import wrapt
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named wrapt

The problem is that the package inside the checkout is named src instead of wrapt

The C extension compilation fails on Mac OS X Mavericks

When I try to install the library through setup.py on Mac OS X Mavericks (10.9.2), the C extension won't compile as per the log below.

< christopher ~/src/3rdparty/wrapt > sudo python setup.py install
running install
running build
running build_py
creating build
creating build/lib.macosx-10.9-intel-2.7
creating build/lib.macosx-10.9-intel-2.7/wrapt
copying src/__init__.py -> build/lib.macosx-10.9-intel-2.7/wrapt
copying src/arguments.py -> build/lib.macosx-10.9-intel-2.7/wrapt
copying src/decorators.py -> build/lib.macosx-10.9-intel-2.7/wrapt
copying src/importer.py -> build/lib.macosx-10.9-intel-2.7/wrapt
copying src/six.py -> build/lib.macosx-10.9-intel-2.7/wrapt
copying src/wrappers.py -> build/lib.macosx-10.9-intel-2.7/wrapt
running build_ext
building 'wrapt._wrappers' extension
creating build/temp.macosx-10.9-intel-2.7
creating build/temp.macosx-10.9-intel-2.7/src
cc -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -arch i386 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -mno-fused-madd -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE -arch x86_64 -arch i386 -pipe -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c src/_wrappers.c -o build/temp.macosx-10.9-intel-2.7/src/_wrappers.o
clang: error: unknown argument: '-mno-fused-madd' [-Wunused-command-line-argument-hard-error-in-future]
clang: note: this will be a hard error (cannot be downgraded to a warning) in the future
***************************************************************************

WARNING: The C extension component for wrapt could not be compiled.

INFO: Trying to build without extensions.

***************************************************************************
running install
running build
running build_py
creating build/lib
creating build/lib/wrapt
copying src/__init__.py -> build/lib/wrapt
copying src/arguments.py -> build/lib/wrapt
copying src/decorators.py -> build/lib/wrapt
copying src/importer.py -> build/lib/wrapt
copying src/six.py -> build/lib/wrapt
copying src/wrappers.py -> build/lib/wrapt
running install_lib
creating /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/__init__.py -> /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/arguments.py -> /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/decorators.py -> /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/importer.py -> /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/six.py -> /Library/Python/2.7/site-packages/wrapt
copying build/lib/wrapt/wrappers.py -> /Library/Python/2.7/site-packages/wrapt
byte-compiling /Library/Python/2.7/site-packages/wrapt/__init__.py to __init__.pyc
byte-compiling /Library/Python/2.7/site-packages/wrapt/arguments.py to arguments.pyc
byte-compiling /Library/Python/2.7/site-packages/wrapt/decorators.py to decorators.pyc
byte-compiling /Library/Python/2.7/site-packages/wrapt/importer.py to importer.pyc
byte-compiling /Library/Python/2.7/site-packages/wrapt/six.py to six.pyc
byte-compiling /Library/Python/2.7/site-packages/wrapt/wrappers.py to wrappers.pyc
running install_egg_info
Writing /Library/Python/2.7/site-packages/wrapt-1.8.0-py2.7.egg-info
***************************************************************************

WARNING: The C extension component for wrapt could not be compiled.

INFO: Only pure Python version of package installed.

***************************************************************************

Signature preservation when using adapters not working for bound functions.

Signature preservation when using adapters works where decorating a normal function, but where decorating an instance or class method, when the bound method is accessed, then signature preservation, as checked by inspect.getargspec() is not giving the correct result on the bound method.

Note this only applies to where an adapter function is being supplied to the decorator for signature changing decorators. It is not an issue for a normal decorator which isn't changing the signature.

kwargs is empty with decorator

Python : 2.7, wrapt (1.10.5)

Test with the following code:

import wrapt

@wrapt.decorator
def test_wrapt(wrapped, instance, *args, **kwargs):
    print('check args:')
    print(args)
    print('check kwargs')
    print(kwargs)
    return wrapped(*args, **kwargs)

class TestObject(object):

    @test_wrapt
    def test(self, x, y, z):
        return x + y + z


test = TestObject()
test.test(x=2,y=2,z=4)

The output is:

check args:
((), {'y': 2, 'x': 2, 'z': 4})
check kwargs
{}
Traceback (most recent call last):
  File "test_wrapt.py", line 20, in <module>
    test.test(x=2,y=2,z=4)
  File "test_wrapt.py", line 10, in test_wrapt
    return wrapped(*args, **kwargs)
TypeError: test() takes exactly 4 arguments (3 given)

wrapt.decorator does not work with super

import wrapt

@wrapt.decorator
def wrap(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)


class Class1(object):
    def __init__(self):
        print '1'

@wrap
class Class2(Class1):
    def __init__(self):
        print '2'
        super(Class2, self).__init__()

d=Class2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "wrap.py", line 22, in <module>
    d=Class2()
  File "wrap.py", line 6, in wrap
    return wrapped(*args, **kwargs)
  File "wrap.py", line 17, in __init__
    super(Class2, self).__init__()
TypeError: must be type, not FunctionWrapper

Can't inspect decorated class

In Python 2.7.5, I can't seem to run inspect.getmembers() on a class that has been decorated.

import inspect
import wrapt

@wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)

@pass_through
class Class(object):
    pass

for name, item in inspect.getmembers( Class ):
    print( item )

This snippet will return the following traceback.

Traceback (most recent call last):
  File "/Users/christopher/Desktop/decorator/test.py", line 12, in <module>
    for name, item in inspect.getmembers( Class ):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 253, in getmembers
    value = getattr(object, key)
TypeError: descriptor '__weakref__' for '_ObjectProxyMethods' objects doesn't apply to 'FunctionWrapper' object

The same snippet is working in Python 3.3.4 though.

Cheers,
Christopher.

Trouble converting decorator to wrapt

I've been trying to convert my decorator to wrapt so that I can support method instances, but can't work out how to do it all...

decorator -
https://github.com/stuaxo/mnd/blob/master/mnd/dispatch.py#L83

and some usage examples -
https://github.com/stuaxo/mnd

My decorator adds a function to a 'dispatcher', when you pass in matching arguments to the dispatcher, it will call the function.

At the time of calling, I guess I can use wrapt somehow to pass the right params (at the moment I can't wrap methods as self is missing) ... I got super confused trying to use wrapt with this, I guess my use case is slightly unusual.

  • any clues as to how I can use wrapt here ?

Coroutine decorator

Wrapt may not be the best place for this, but it could be nice to add support for having a decorator that declares setup and teardown activities for a coroutine/generator.

This can be acheived pretty simply with something like

@wrapt.decorator
def decorate_coroutine(function, instance, args, kwargs):
    setup()
    coroutine = function(*args, **kwargs)
    to_yield = next(coroutine)
    while True:
        try:
            to_send = yield to_yield
        except Exception:
            to_yield = coroutine.throw(*sys.exc_info())
        else:
            to_yield = coroutine.send(to_send)
    teardown()

but it might be nice to have some interface like

@wrapt.decorator(coroutine=True)
def decorate_coroutine(function, instance, args, kwargs):
    setup()
    result = yield function(*args, **kwargs)
    teardown(result)

Which would also allow easy context manager applications like

@wrapt.decorator(coroutine=True)
def decorate_coroutine(function, instance, args, kwargs):
    with context_manager():
        yield self.__handle_function(cassette, function, args, kwargs)

Wrapping bytearrays under ctypes

The following raises TypeError: expected an object with a writable buffer interface:

ctypes.c_char.from_buffer(wrapt.ObjectProxy(bytearray(b'12345')))

This is a corner case I've encountered in my own proxy implementation which also doesn't work, and doesn't seem to be possible to wrap in pure python without copying the bytearray. Now that I've discovered wrapt which is written in C. I'm hoping it can be implemented.

Please ship tests to pypi

With the reorganisation of the package between 1.10.5/6 the tests where dropped from the pypi package. Please add them back so that downstream distro maintainers can test.

Question on decorating @property methods

A question recently came up on https://review.openstack.org/#/c/206253/ about using the universal wrapt decorator on @Property methods, and this doesn't seem possible (see comments in that review); I'm wondering if u know anyway that it is possible to make it work? If not that's fine to, just thought u might have some thoughts there?

That code @ https://github.com/openstack/debtcollector/blob/master/debtcollector/removals.py#L28 would be the 'universal remove/deprecate thing function' but in that review it doesn't seem possible to use it on @Property methods. Thoughts?

wrapt functions are not dill-able

It would be extremely useful if wrapt functions were serializable in some way. Although pickle support is unlikely given that pickle doesn't even handle undecorated functions, I hoped the more capable dill may have been up to the task, as dill is capable of handling the decorator pattern in general:

def basic_pass_through(f):

    def g(*args, **kwargs):
        return f(*args, **kwargs)

    return g

@basic_pass_through
def basic_function(a):
    return 2 * a

import dill
f_str = dill.dumps(basic_function)
function_back = dill.loads(f_str)

assert function_back(2) == 4

unfortunately, with wrapt we get:

import wrapt

@wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)

@pass_through
def function():
    pass

import dill
f_str = dill.dumps(function)
function_back = dill.loads(f_str)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-f472b826a7de> in <module>()
     13 
     14 f_str = dill.dumps(function)
---> 15 function_back = dill.loads(f_str)

.../lib/python2.7/site-packages/dill/dill.pyc in loads(str)
    158     """unpickle an object from a string"""
    159     file = StringIO(str)
--> 160     return load(file)
    161 
    162 # def dumpzs(obj, protocol=None):

.../lib/python2.7/site-packages/dill/dill.pyc in load(file)
    148     pik = Unpickler(file)
    149     pik._main_module = _main_module
--> 150     obj = pik.load()
    151     if type(obj).__module__ == _main_module.__name__: # point obj class to main
    152         try: obj.__class__ == getattr(pik._main_module, type(obj).__name__)

.../lib/python2.7/pickle.pyc in load(self)
    856             while 1:
    857                 key = read(1)
--> 858                 dispatch[key](self)
    859         except _Stop, stopinst:
    860             return stopinst.value

...lib/python2.7/pickle.pyc in load_newobj(self)
   1081         args = self.stack.pop()
   1082         cls = self.stack[-1]
-> 1083         obj = cls.__new__(cls, *args)
   1084         self.stack[-1] = obj
   1085     dispatch[NEWOBJ] = load_newobj

TypeError: Required argument 'code' (pos 1) not found

I would happily prepare a PR for wrapt which enabled support for dill serialization but I'm not too sure where to start. Certainly by inspection we can see that dill has failed to serialize the wrapt function in the first place:

>> print(f_str)
'\x80\x02cdill.dill\n_load_type\nq\x00U\x0cFunctionTypeq\x01\x85q\x02Rq\x03)\x81q\x04}q\x05b.'

I don't know if I would be better off bringing this to the dill author @mmckerns first or yourself, but if anyone can shed any light I would love to get this working.

Only getter property in custom wrapper works, not setter/deleter.

In code:

import wrapt

class CustomProxy(wrapt.ObjectProxy):

    def __init__(self, wrapped):
        super(CustomProxy, self).__init__(wrapped)
        self._self_attribute = 1

    @property
    def attribute(self):
        return self._self_attribute

    @attribute.setter
    def attribute(self, value):
        self._self_attribute = value

    @attribute.deleter
    def attribute(self):
        del self._self_attribute

p = CustomProxy(None)

print p.attribute
p.attribute = 2
del p.attribute

accessing 'attribute' from the proxy works, but updating the attribute or deleting the attribute flows through and is attempted on the wrapped object.

metaclass conflict when deriving from decorated classes

Hi Graham,

We use wrapt extensively in our codebase, adopting it as our defacto standard for all decoration - great module!

Unfortunately I've just hit an edge-case with someone trying to decorate a class using wrapt and then trying to derive a child class - this does not work.

Here's a simplified example:

import wrapt

@wrapt.decorator
def dummy_wrap(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)

@dummy_wrap
class A(object):
    pass

class B(A):
    pass

With output:

TypeError: Error when calling the metaclass bases metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Do you foresee a way of handling this case?

Thanks,
Matt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.