A Python module for decorators, wrappers and monkey patching.

Related tags

Miscellaneous wrapt
Overview

wrapt

Travis Appveyor Coveralls 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.

Comments
  • segfault during our app test during GC with wrapt in the traceback

    segfault during our app test during GC with wrapt in the traceback

    Not 100% sure that it is wrapt to blame, but we only recently decided to check out wrapt as a part of bigger refactoring PR. Unfortunately our tests started to segfault on some test setups (some buildbot docker environments, but not the other, travis). With wrapt being one of the few libraries we use which has a binary extension, and having not seen similar crash before, and having wrapt's functions within the stack at crash time it is likely to assume that the reason is somewhere within wrapt. Here is an extract from the 'gdb bt' and more detail (valgrind output etc) could be found at https://github.com/datalad/datalad/issues/1382

    (gdb) bt
    #0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:58
    #1  0x00007ffff6f2d40a in __GI_abort () at abort.c:89
    #2  0x00007ffff6f24e47 in __assert_fail_base (fmt=<optimized out>, assertion=assertion@entry=0x5555557fae87 "gc->gc.gc_refs != 0", file=file@entry=0x5555557fae40 "../Modules/gcmodule.c", line=line@entry=351, 
        function=function@entry=0x5555557fb2d8 <__PRETTY_FUNCTION__.9755> "update_refs") at assert.c:92
    #3  0x00007ffff6f24ef2 in __GI___assert_fail (assertion=0x5555557fae87 "gc->gc.gc_refs != 0", file=0x5555557fae40 "../Modules/gcmodule.c", line=351, function=0x5555557fb2d8 <__PRETTY_FUNCTION__.9755> "update_refs")
        at assert.c:101
    #4  0x0000555555712c3b in update_refs (containers=0x555555a7cd20 <generations+96>) at ../Modules/gcmodule.c:351
    #5  0x0000555555713e48 in collect (generation=2) at ../Modules/gcmodule.c:924
    #6  0x000055555571432b in gc_collect (self=0x0, args=(), kws=0x0) at ../Modules/gcmodule.c:1121
    #7  0x00005555556187a8 in PyCFunction_Call (func=<built-in function collect>, arg=(), kw=0x0) at ../Objects/methodobject.c:85
    ...
    #29 0x000055555561f0bb in _Py_Dealloc (
        op=<Dataset(_cfg=None, _repo=<GitRepo(realpath=<unknown at remote 0x7ffff354d9d0>, _GIT_COMMON_OPTIONS=['-c', 'receive.autogc=0', '-c', 'gc.auto=0'], cmd_call_wrapper=<unknown at remote 0x7ffff3580ec0>, repo=<Repo(git=<Git at remote 0x7ffff3548cd8>, working_dir='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive', _bare=False, git_dir='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive/.git', odb=<GitCmdObjectDB(_ostream=None, _hexsha_to_file={}, _fd_open_flags=262144, _root_path='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive/.git/objects', _git=<...>) at remote 0x7ffff3a8bfb0>, _working_tree_dir='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive') at remote 0x7ffff3a8bd10>, _cfg=None, path='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive') at remote 0x7ffff373c370>, _id=None, _path='/tmp/datalad_temp_test_install_crcnsrR0VqX/all-nonrecursive') at remote 0x7ffff4b8e8b0>) at ../Objects/object.c:2262
    #30 0x00007ffff4e323e4 in WraptFunctionWrapperBase_clear (self=0x7ffff3578ba0) at src/wrapt/_wrappers.c:1898
    #31 0x00007ffff4e32657 in WraptFunctionWrapperBase_dealloc (self=0x7ffff3578ba0) at src/wrapt/_wrappers.c:1911
    #32 0x000055555561f0bb in _Py_Dealloc (op=<BoundFunctionWrapper at remote 0x7ffff3578ba0>) at ../Objects/object.c:2262
    #33 0x00005555556c52fd in call_function (pp_stack=0x7fffffff6030, oparg=1) at ../Python/ceval.c:4385
    ...
    

    does anything ring any bell? if desired, I could provide a reproducible Dockefile to bring you to the same point. This was tested using current wrapt master as of 1.10.10 release

    opened by yarikoptic 63
  • Missing Python 2.7 windows wheel on version 1.13.1

    Missing Python 2.7 windows wheel on version 1.13.1

    I noticed that there are Python 2.7 wheels built for mac and linux for this release, was wondering if it's possible for a Python 2.7 wheel for windows to be added to the PyPI download files for this release.

    Thanks!

    opened by pythonoi 33
  • ObjectProxy objects are not copyable

    ObjectProxy objects are not copyable

    I was trying to use ObjectProxy in a context where the object in question sometimes gets copied by copy.copy, e.g.:

    from wrapt import ObjectProxy
    import copy
    print(copy.copy(ObjectProxy(1)))
    

    which raises an exception:

    ValueError                                Traceback (most recent call last)
    <ipython-input-29-184d66a435a2> in <module>()
    ----> 1 print(copy.copy(ObjectProxy(1)))
    
    /Users/ryan/.pyenv/versions/3.5.2/lib/python3.5/copy.py in copy(x)
        103                 raise Error("un(shallow)copyable object of type %s" % cls)
        104
    --> 105     return _reconstruct(x, rv, 0)
        106
        107
    
    /Users/ryan/.pyenv/versions/3.5.2/lib/python3.5/copy.py in _reconstruct(x, info, deep, memo)
        296         if deep:
        297             state = deepcopy(state, memo)
    --> 298         if hasattr(y, '__setstate__'):
        299             y.__setstate__(state)
        300         else:
    
    ValueError: wrapper has not been initialized
    

    To fix this, I implemented the __copy__() and __deepcopy__() methods in a subclass of ObjectProxy:

    from wrapt import ObjectProxy
    import copy
    
    class CopyableObjectProxy(ObjectProxy):
    
        def __copy__(self):
            copied_wrapped = copy.copy(self.__wrapped__)
            return self.__class__(copied_wrapped)
    
        def __deepcopy__(self):
            copied_wrapped = copy.deepcopy(self.__wrapped__)
            return self.__class__(copied_wrapped)
    
    x = 1
    xp = ObjectProxy(x)
    xcp = CopyableObjectProxy(x)
    
    print(copy.copy(x))
    try:
        print(copy.copy(xp))
    except ValueError:
        print("Copy of xp failed")
    print(copy.copy(xcp))
    

    However, this presumably will not work for more complex proxy classes that have their own state that also needs copying. Is there a way to implement this generally for any proxy object, or does every subclass of ObjectProxy need to provide a custom __copy__() method if it wants to be copyable?

    opened by DarwinAwardWinner 25
  • Installation can fail in Python3 in Windows due to UnicodeDecodeError

    Installation can fail in Python3 in Windows due to UnicodeDecodeError

    $ pip install wrapt
    

    Will fail in Windows using a Visual Studio in some languages, because it will try to output some accented characters, and pip will fail with UnicodeDecodeError to decode them properly, and wrapt will not be properly installed.

    Exception:
    Traceback (most recent call last):
      File "c:\users\memsharded\envs\conan3\lib\site-packages\pip\compat\__init__.py", line 73, in console_to_str
        return s.decode(sys.__stdout__.encoding)
    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf3 in position 11: invalid continuation byte
    

    The failure is in pip/compat/__init__.py file:

    if sys.version_info >= (3,):
        def console_to_str(s):
            try:
                return s.decode(sys.__stdout__.encoding)
            except UnicodeDecodeError:
                   return s.decode('utf_8')
    

    I have just workarounded it with:

    if sys.version_info >= (3,):
        def console_to_str(s):
            try:
                return s.decode(sys.__stdout__.encoding)
            except UnicodeDecodeError:
                try:
                   return s.decode('utf_8')
                except Exception as e:
                   return "Cannot decode this string"
    

    It seems this will be improved in pip 10 (not yet released): https://github.com/pypa/pip/issues/4110#issuecomment-304476523

    opened by memsharded 20
  • Support for type hints with adapters

    Support for type hints with adapters

    If we have a function definition with type hints, e.g.

    import A
    
    def test_function(x: A.Dog, b: A.Cat):
    	...
    

    the exec_ call in decorator will fail since A is not in the globals dictionary. This PR updates ns to be the globals from the calling module so that the imported packages are available and the exec works as expected.

    This may seem a bit hacky, but it's the only solution I can find. If you have any suggestions, I am all ears.

    opened by varunagrawal 19
  • regression with python3.7: AttributeError: 'zipimport.zipimporter' object has no attribute 'create_module'

    regression with python3.7: AttributeError: 'zipimport.zipimporter' object has no attribute 'create_module'

    After updating to wrapt 1.14.0, I started seeing issues with pip installs on python3.7:

    
    Installing build dependencies: started
    Installing build dependencies: finished with status 'error'
    error: subprocess-exited-with-error
    
    × pip subprocess to install build dependencies did not run successfully.
    │ exit code: 1
    ╰─> [58 lines of output]
        Traceback (most recent call last):
          File "/opt/miniconda/envs/foo/lib/python3.7/runpy.py", line 193, in _run_module_as_main
            "__main__", mod_spec)
          File "/opt/miniconda/envs/foo/lib/python3.7/runpy.py", line 85, in _run_code
            exec(code, run_globals)
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/__main__.py", line 29, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/cli/main.py", line 9, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/cli/autocompletion.py", line 10, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/cli/main_parser.py", line 8, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/cli/cmdoptions.py", line 23, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/cli/parser.py", line 12, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/configuration.py", line 20, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_internal/exceptions.py", line 13, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_vendor/requests/__init__.py", line 135, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 668, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 638, in _load_backward_compatible
          File "/tmp/pip-standalone-pip-8p809dqm/__env_pip__.zip/pip/_vendor/requests/utils.py", line 27, in <module>
          File "<frozen importlib._bootstrap>", line 983, in _find_and_load
          File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
          File "<frozen importlib._bootstrap>", line 670, in _load_unlocked
          File "<frozen importlib._bootstrap>", line 583, in module_from_spec
          File "/opt/miniconda/envs/shrek/lib/python3.7/site-packages/wrapt/importer.py", line 166, in create_module
            return self.loader.create_module(spec)
        AttributeError: 'zipimport.zipimporter' object has no attribute 'create_module'
        [end of output]
    
    

    I assume it could be due to: https://github.com/GrahamDumpleton/wrapt/commit/53323ba4b92f88dab5a8ec3262a1c1f1e979a3e2

    which introduced this code:

        def create_module(self, spec):
             return self.loader.create_module(spec)
    

    However, the zipimporter module's create_module function was only added in python 3.10: https://docs.python.org/3/library/zipimport.html#zipimport.zipimporter.create_module

    Reverting wrapt to 1.13.3 resolved the pip issues again.

    opened by rggjan 18
  • No embedded six.py and fix for python 3.2 and 3.4

    No embedded six.py and fix for python 3.2 and 3.4

    The current version uses an embedded version of six.py, and do not support 3.2 (needed for Debian Wheezy), and also do not work with Python 3.4. This patch-set fixes all of this.

    opened by thomasgoirand 18
  • No module named 'wrapt'

    No module named 'wrapt'

    Hi!

    I face the issue, No module named 'wrapt', even though I already installed wrapt package. conda install wrapt pip3 install -U --no-cache-dir wrapt didn't help me.

    This is my libraries.

    absl-py==0.15.0 albumentations==1.0.0 astunparse==1.6.3 cachetools==4.2.4 certifi==2021.10.8 charset-normalizer==2.0.7 cv==1.0.0 cycler==0.11.0 flatbuffers==1.12 fonttools==4.28.1 gast==0.3.3 google-auth==2.3.3 google-auth-oauthlib==0.4.6 google-pasta==0.2.0 grpcio==1.32.0 h5py==2.10.0 idna==3.3 imageio==2.11.0 importlib-metadata==4.8.2 keras==2.7.0 Keras-Preprocessing==1.1.2 kiwisolver==1.3.2 libclang==12.0.0 Markdown==3.3.6 matplotlib==3.5.0 mkl-fft==1.3.1 mkl-random @ file:///C:/ci/mkl_random_1626186163140/work mkl-service==2.4.0 monotonic==1.6 netifaces==0.11.0 networkx==2.6.3 numpy @ file:///C:/ci/numpy_and_numpy_base_1634106873805/work oauthlib==3.1.1 olefile==0.46 opencv-python==4.5.4.58 opencv-python-headless==4.5.4.58 opt-einsum==3.3.0 packaging==21.3 Pillow==8.4.0 protobuf==3.19.1 pyasn1==0.4.8 pyasn1-modules==0.2.8 pyparsing==3.0.6 python-dateutil==2.8.2 PyWavelets==1.2.0 PyYAML==6.0 requests==2.26.0 requests-oauthlib==1.3.0 rsa==4.7.2 scikit-image==0.18.3 scipy==1.7.2 setuptools-scm==6.3.2 six @ file:///tmp/build/80754af9/six_1623709665295/work tensorboard==2.7.0 tensorboard-data-server==0.6.1 tensorboard-plugin-wit==1.8.0 tensorflow==2.7.0 tensorflow-estimator==2.7.0 tensorflow-gpu==2.7.0 tensorflow-io-gcs-filesystem==0.22.0 termcolor==1.1.0 tifffile==2021.11.2 tomli==1.2.2 typing-extensions==3.7.4.3 urllib3==1.26.7 Werkzeug==2.0.2 wincertstore==0.2 wrapt==1.13.3 zipp==3.6.0

    opened by dongyun-kim-arch 16
  • Support the dynamic generation of signatures for signature changing decorators

    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.

    opened by IvanMalison 14
  • Performing computations after the decorator is applied to the function but before the decorated function is called

    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?

    opened by ceridwen 13
  • Remove unnecessary check for __wrapped__

    Remove unnecessary check for __wrapped__

    It's always going to be on the type object (thus get caught in the following check).

    At least I think so :grin: - please let me know if I missed some edge-case here.

    opened by ionelmc 12
  • New release/CI run to get Python 3.11 wheels

    New release/CI run to get Python 3.11 wheels

    Description

    Since https://github.com/GrahamDumpleton/wrapt/commit/cd4498354e647c0f9e158e92fd94555c20a381e3 has been pushed, CI workflow should be able to handle CPython 3.11 wheels, but I guess workflow should be triggered or a new release should be made so that they are built and uploaded to PyPi.

    Would it be possible to handle this ? Thank you very much :smile: cc @GrahamDumpleton

    opened by stegayet 0
  • chore(ci): CI improvements

    chore(ci): CI improvements

    Description

    This PR is about having some CI improvements

    Details

    • First commit upgrades all GH actions
    • Third commit enables running tests on Python 3.11 final release
    opened by stegayet 1
  • post_import_hooks break pkg_resources in wrapt 1.14

    post_import_hooks break pkg_resources in wrapt 1.14

    The simplest way to retrocede this is:

    import wrapt
    wrapt.register_post_import_hook(lambda m: print('loaded', m), 'asyncio')
    import asyncio, pkg_resources
    print(pkg_resources.get_provider('asyncio').has_resource('events.py'))
    

    This fails with the following traceback:

    Traceback (most recent call last):
      File "t.py", line 4, in <module>
        print(pkg_resources.get_provider('asyncio').has_resource('events.py'))
      File "/home/elt/.local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1404, in has_resource
        return self._has(self._fn(self.module_path, resource_name))
      File "/home/elt/.local/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1457, in _has
        "Can't perform this operation for unregistered loader type"
    NotImplementedError: Can't perform this operation for unregistered loader type
    

    The reason is that pkg_resources.get_provider() could not find a usable provider, because the __loader__ on the asyncio module was left as _ImportHookChainedLoader instead of being set to the standard SourceFileLoader. Leaving wrapt's internal loader is leaking implementation details. This did not happen previously because _ImportHookChainedLoader implemented load_module by calling to the original loader. The original loader set __loader__ to itself. Now that _ImportHookChainedLoader implements create_module, the import machinery sets __loader__ to the wrapt's loader.

    opened by eltoder 0
  • Error installing with pip and python 3.11.0

    Error installing with pip and python 3.11.0

    python 3.11.0 on fresh virtualenv (linux):

    `Installing collected packages: wrapt, pyparsing, async-timeout, packaging, deprecated, redis Running setup.py install for wrapt ... error error: subprocess-exited-with-error

    × Running setup.py install for wrapt did not run successfully. │ exit code: 1 ╰─> [56 lines of output] running install running build running build_py creating build creating build/lib.linux-x86_64-3.11 creating build/lib.linux-x86_64-3.11/wrapt copying src/wrapt/decorators.py -> build/lib.linux-x86_64-3.11/wrapt copying src/wrapt/wrappers.py -> build/lib.linux-x86_64-3.11/wrapt copying src/wrapt/arguments.py -> build/lib.linux-x86_64-3.11/wrapt copying src/wrapt/importer.py -> build/lib.linux-x86_64-3.11/wrapt copying src/wrapt/init.py -> build/lib.linux-x86_64-3.11/wrapt running build_ext building 'wrapt._wrappers' extension creating build/temp.linux-x86_64-3.11 creating build/temp.linux-x86_64-3.11/src creating build/temp.linux-x86_64-3.11/src/wrapt gcc -pthread -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -fPIC -I/home/rico/code/easywebsocket/include -I/usr/local/include/python3.11 -c src/wrapt/_wrappers.c -o build/temp.linux-x86_64-3.11/src/wrapt/_wrappers.o src/wrapt/_wrappers.c: In function ‘WraptFunctionWrapperBase_instancecheck’: src/wrapt/_wrappers.c:2550:15: warning: unused variable ‘object’ [-Wunused-variable] 2550 | PyObject *object = NULL; | ^~~~~~ gcc -pthread -shared build/temp.linux-x86_64-3.11/src/wrapt/_wrappers.o -o build/lib.linux-x86_64-3.11/wrapt/_wrappers.cpython-311-x86_64-linux-gnu.so running install_lib Traceback (most recent call last): File "", line 2, in File "", line 34, in File "/tmp/pip-install-1spriocg/wrapt_e6e1bbefd7524c62ab63fd869239a10f/setup.py", line 41, in setuptools.setup( File "/home/rico/code/easywebsocket/lib/python3.11/site-packages/setuptools/init.py", line 162, in setup return distutils.core.setup(**attrs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/distutils/core.py", line 148, in setup dist.run_commands() File "/usr/local/lib/python3.11/distutils/dist.py", line 966, in run_commands self.run_command(cmd) File "/usr/local/lib/python3.11/distutils/dist.py", line 985, in run_command cmd_obj.run() File "/home/rico/code/easywebsocket/lib/python3.11/site-packages/setuptools/command/install.py", line 61, in run return orig.install.run(self) ^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/distutils/command/install.py", line 580, in run self.run_command(cmd_name) File "/usr/local/lib/python3.11/distutils/cmd.py", line 313, in run_command self.distribution.run_command(command) File "/usr/local/lib/python3.11/distutils/dist.py", line 984, in run_command cmd_obj.ensure_finalized() File "/usr/local/lib/python3.11/distutils/cmd.py", line 107, in ensure_finalized self.finalize_options() File "/home/rico/code/easywebsocket/lib/python3.11/site-packages/setuptools/command/install_lib.py", line 17, in finalize_options self.set_undefined_options('install',('install_layout','install_layout')) File "/usr/local/lib/python3.11/distutils/cmd.py", line 290, in set_undefined_options setattr(self, dst_option, getattr(src_cmd_obj, src_option)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/distutils/cmd.py", line 103, in getattr raise AttributeError(attr) AttributeError: install_layout. Did you mean: 'install_platlib'? [end of output]

    note: This error originates from a subprocess, and is likely not a problem with pip. error: legacy-install-failure

    × Encountered error while trying to install package. ╰─> wrapt `

    opened by ilrico 2
  • Fix threading issues with register_post_import_hook

    Fix threading issues with register_post_import_hook

    1. Add a missing lock to ImportHookFinder.find_spec(). It should have the same lock as find_module() since it accesses _post_import_hooks.

    2. Fix a deadlock between notify_module_loaded() and ImportHookFinder. This occurs because the former is calling import hooks under _post_import_hooks_lock. If a hook is making any imports itself while another thread is also making imports, the thread can grab the import lock and wait for _post_import_hooks_lock in ImportHookFinder, while the hook already has _post_import_hooks_lock and is waiting for the import lock. This results in a deadlock. The solution is to call the hooks outside of _post_import_hooks_lock.

    Same for register_post_import_hook() and ImportHookFinder.

    1. Also fix an issue if register_post_import_hook() was called after a module was already imported but was later removed from sys.modules. This also makes the logic simpler.
    opened by eltoder 10
Owner
Graham Dumpleton
Graham Dumpleton
tgEasy | Easy for a Brighter Shine | Monkey Patcher Addon for Pyrogram

tgEasy | Easy for a Brighter Shine | Monkey Patcher Addon for Pyrogram

Jayant Hegde Kageri 35 Nov 12, 2022
Strong Typing in Python with Decorators

typy Strong Typing in Python with Decorators Description This light-weight library provides decorators that can be used to implement strongly-typed be

Ekin 0 Feb 6, 2022
Wrappers around the most common maya.cmds and maya.api use cases

Maya FunctionSet (maya_fn) A package that decompose core maya.cmds and maya.api features to a set of simple functions. Tests The recommended approach

Ryan Porter 9 Mar 12, 2022
Tool to generate wrappers for Linux libraries allowing for dlopen()ing them without writing any boilerplate

Dynload wrapper This program will generate a wrapper to make it easy to dlopen() shared objects on Linux without writing a ton of boilerplate code. Th

Hein-Pieter van Braam 25 Oct 24, 2022
Module for remote in-memory Python package/module loading through HTTP/S

httpimport Python's missing feature! The feature has been suggested in Python Mailing List Remote, in-memory Python package/module importing through H

John Torakis 220 Dec 17, 2022
Run python scripts and pass data between multiple python and node processes using this npm module

Run python scripts and pass data between multiple python and node processes using this npm module. process-communication has a event based architecture for interacting with python data and errors inside nodejs.

Tyler Laceby 2 Aug 6, 2021
PyPIContents is an application that generates a Module Index from the Python Package Index (PyPI) and also from various versions of the Python Standard Library.

PyPIContents is an application that generates a Module Index from the Python Package Index (PyPI) and also from various versions of the Python Standar

Collage Labs 10 Nov 19, 2022
Python 3.9.4 Graphics and Compute Shader Framework and Primitives with no external module dependencies

pyshader Python 3.9.4 Graphics and Compute Shader Framework and Primitives with no external module dependencies Fully programmable shader model (even

Alastair Cota 1 Jan 11, 2022
Python communism - A module for initiating the communist revolution in each of our python modules

Python communist revolution A man once said to abolish the classes or something

null 758 Jan 3, 2023
Python screenshot library, replacement for the Pillow ImageGrab module on Linux.

tldr: Use Pillow The pyscreenshot module is obsolete in most cases. It was created because PIL ImageGrab module worked on Windows only, but now Linux

null 455 Dec 24, 2022
An ultra fast cross-platform multiple screenshots module in pure Python using ctypes.

Python MSS from mss import mss # The simplest use, save a screen shot of the 1st monitor with mss() as sct: sct.shot() An ultra fast cross-platfo

Mickaël Schoentgen 799 Dec 30, 2022
This module is for finding the execution time of a whole python program

exetime 3.8 This module is for finding the execution time of a whole program How to install $ pip install exetime Contents: General Information Instru

Saikat Das 4 Oct 18, 2021
Python module to work with Magneto Database directly without using broken Magento 2 core

Python module to work with Magneto Database directly without using broken Magento 2 core

Egor Shitikov 13 Nov 10, 2022
A module comment generator for python

Module Comment Generator The comment style is as a tribute to the comment from the RA . The comment generator can parse the ast tree from the python s

飘尘 1 Oct 21, 2021
This python module allows to extract data from the RAW-file-format produces by devices from Thermo Fisher Scientific.

fisher_py This Python module allows access to Thermo Orbitrap raw mass spectrometer files. Using this library makes it possible to automate the analys

null 8 Oct 14, 2022
Module for working with the site dnevnik.ru with python

dnevnikru Module for working with the site dnevnik.ru with python Dnevnik object accepts login and password from the dnevnik.ru account Methods: homew

Aleksandr 21 Nov 21, 2022
Python module for creating the circuit simulation definitions for Elmer FEM

elmer_circuitbuilder Python module for creating the circuit simulation definitions for Elmer FEM. The circuit definitions enable easy setup of coils (

null 5 Oct 3, 2022
A python mathematics module

A python mathematics module

Fayas Noushad 4 Nov 28, 2021
A lightweight Python module to interact with the Mitre Att&ck Enterprise dataset.

enterpriseattack - Mitre's Enterprise Att&ck A lightweight Python module to interact with the Mitre Att&ck Enterprise dataset. Built to be used in pro

xakepnz 7 Jan 1, 2023