about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/wrapt
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/wrapt')
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/__init__.py30
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/__wrapt__.py14
-rwxr-xr-x.venv/lib/python3.12/site-packages/wrapt/_wrappers.cpython-312-x86_64-linux-gnu.sobin0 -> 221464 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/arguments.py38
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/decorators.py541
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/importer.py295
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/patches.py141
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/weakrefs.py98
-rw-r--r--.venv/lib/python3.12/site-packages/wrapt/wrappers.py821
9 files changed, 1978 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/wrapt/__init__.py b/.venv/lib/python3.12/site-packages/wrapt/__init__.py
new file mode 100644
index 00000000..cb00b5a2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/__init__.py
@@ -0,0 +1,30 @@
+__version_info__ = ('1', '17', '2')
+__version__ = '.'.join(__version_info__)
+
+from .__wrapt__ import (ObjectProxy, CallableObjectProxy, FunctionWrapper,
+        BoundFunctionWrapper, PartialCallableObjectProxy)
+
+from .patches import (resolve_path, apply_patch, wrap_object, wrap_object_attribute,
+        function_wrapper, wrap_function_wrapper, patch_function_wrapper,
+        transient_function_wrapper)
+
+from .weakrefs import WeakFunctionProxy
+
+from .decorators import (adapter_factory, AdapterFactory, decorator,
+        synchronized)
+
+from .importer import (register_post_import_hook, when_imported,
+        notify_module_loaded, discover_post_import_hooks)
+
+# Import of inspect.getcallargs() included for backward compatibility. An
+# implementation of this was previously bundled and made available here for
+# Python <2.7. Avoid using this in future.
+
+from inspect import getcallargs
+
+# Variant of inspect.formatargspec() included here for forward compatibility.
+# This is being done because Python 3.11 dropped inspect.formatargspec() but
+# code for handling signature changing decorators relied on it. Exposing the
+# bundled implementation here in case any user of wrapt was also needing it.
+
+from .arguments import formatargspec
diff --git a/.venv/lib/python3.12/site-packages/wrapt/__wrapt__.py b/.venv/lib/python3.12/site-packages/wrapt/__wrapt__.py
new file mode 100644
index 00000000..9933b2c9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/__wrapt__.py
@@ -0,0 +1,14 @@
+import os
+
+from .wrappers import (ObjectProxy, CallableObjectProxy,
+        PartialCallableObjectProxy, FunctionWrapper,
+        BoundFunctionWrapper, _FunctionWrapperBase)
+
+try:
+    if not os.environ.get('WRAPT_DISABLE_EXTENSIONS'):
+        from ._wrappers import (ObjectProxy, CallableObjectProxy,
+            PartialCallableObjectProxy, FunctionWrapper,
+            BoundFunctionWrapper, _FunctionWrapperBase)
+
+except ImportError:
+    pass
diff --git a/.venv/lib/python3.12/site-packages/wrapt/_wrappers.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/wrapt/_wrappers.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..0f5c4233
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/_wrappers.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/wrapt/arguments.py b/.venv/lib/python3.12/site-packages/wrapt/arguments.py
new file mode 100644
index 00000000..032bc059
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/arguments.py
@@ -0,0 +1,38 @@
+# The inspect.formatargspec() function was dropped in Python 3.11 but we need
+# need it for when constructing signature changing decorators based on result of
+# inspect.getargspec() or inspect.getfullargspec(). The code here implements
+# inspect.formatargspec() base on Parameter and Signature from inspect module,
+# which were added in Python 3.6. Thanks to Cyril Jouve for the implementation.
+
+try:
+    from inspect import Parameter, Signature
+except ImportError:
+    from inspect import formatargspec
+else:
+    def formatargspec(args, varargs=None, varkw=None, defaults=None,
+                      kwonlyargs=(), kwonlydefaults={}, annotations={}):
+        if kwonlydefaults is None:
+            kwonlydefaults = {}
+        ndefaults = len(defaults) if defaults else 0
+        parameters = [
+            Parameter(
+                arg,
+                Parameter.POSITIONAL_OR_KEYWORD,
+                default=defaults[i] if i >= 0 else Parameter.empty,
+                annotation=annotations.get(arg, Parameter.empty),
+            ) for i, arg in enumerate(args, ndefaults - len(args))
+        ]
+        if varargs:
+            parameters.append(Parameter(varargs, Parameter.VAR_POSITIONAL))
+        parameters.extend(
+            Parameter(
+                kwonlyarg,
+                Parameter.KEYWORD_ONLY,
+                default=kwonlydefaults.get(kwonlyarg, Parameter.empty),
+                annotation=annotations.get(kwonlyarg, Parameter.empty),
+            ) for kwonlyarg in kwonlyargs
+        )
+        if varkw:
+            parameters.append(Parameter(varkw, Parameter.VAR_KEYWORD))
+        return_annotation = annotations.get('return', Signature.empty)
+        return str(Signature(parameters, return_annotation=return_annotation))
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/wrapt/decorators.py b/.venv/lib/python3.12/site-packages/wrapt/decorators.py
new file mode 100644
index 00000000..c80a4bb7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/decorators.py
@@ -0,0 +1,541 @@
+"""This module implements decorators for implementing other decorators
+as well as some commonly used decorators.
+
+"""
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    string_types = basestring,
+
+    def exec_(_code_, _globs_=None, _locs_=None):
+        """Execute code in a namespace."""
+        if _globs_ is None:
+            frame = sys._getframe(1)
+            _globs_ = frame.f_globals
+            if _locs_ is None:
+                _locs_ = frame.f_locals
+            del frame
+        elif _locs_ is None:
+            _locs_ = _globs_
+        exec("""exec _code_ in _globs_, _locs_""")
+
+else:
+    string_types = str,
+
+    import builtins
+
+    exec_ = getattr(builtins, "exec")
+    del builtins
+
+from functools import partial
+from inspect import isclass
+from threading import Lock, RLock
+
+from .arguments import formatargspec
+
+try:
+    from inspect import signature
+except ImportError:
+    pass
+
+from .__wrapt__ import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy,
+    CallableObjectProxy)
+
+# Adapter wrapper for the wrapped function which will overlay certain
+# properties from the adapter function onto the wrapped function so that
+# functions such as inspect.getargspec(), inspect.getfullargspec(),
+# inspect.signature() and inspect.getsource() return the correct results
+# one would expect.
+
+class _AdapterFunctionCode(CallableObjectProxy):
+
+    def __init__(self, wrapped_code, adapter_code):
+        super(_AdapterFunctionCode, self).__init__(wrapped_code)
+        self._self_adapter_code = adapter_code
+
+    @property
+    def co_argcount(self):
+        return self._self_adapter_code.co_argcount
+
+    @property
+    def co_code(self):
+        return self._self_adapter_code.co_code
+
+    @property
+    def co_flags(self):
+        return self._self_adapter_code.co_flags
+
+    @property
+    def co_kwonlyargcount(self):
+        return self._self_adapter_code.co_kwonlyargcount
+
+    @property
+    def co_varnames(self):
+        return self._self_adapter_code.co_varnames
+
+class _AdapterFunctionSurrogate(CallableObjectProxy):
+
+    def __init__(self, wrapped, adapter):
+        super(_AdapterFunctionSurrogate, self).__init__(wrapped)
+        self._self_adapter = adapter
+
+    @property
+    def __code__(self):
+        return _AdapterFunctionCode(self.__wrapped__.__code__,
+                self._self_adapter.__code__)
+
+    @property
+    def __defaults__(self):
+        return self._self_adapter.__defaults__
+
+    @property
+    def __kwdefaults__(self):
+        return self._self_adapter.__kwdefaults__
+
+    @property
+    def __signature__(self):
+        if 'signature' not in globals():
+            return self._self_adapter.__signature__
+        else:
+            return signature(self._self_adapter)
+
+    if PY2:
+        func_code = __code__
+        func_defaults = __defaults__
+
+class _BoundAdapterWrapper(BoundFunctionWrapper):
+
+    @property
+    def __func__(self):
+        return _AdapterFunctionSurrogate(self.__wrapped__.__func__,
+                self._self_parent._self_adapter)
+
+    @property
+    def __signature__(self):
+        if 'signature' not in globals():
+            return self.__wrapped__.__signature__
+        else:
+            return signature(self._self_parent._self_adapter)
+
+    if PY2:
+        im_func = __func__
+
+class AdapterWrapper(FunctionWrapper):
+
+    __bound_function_wrapper__ = _BoundAdapterWrapper
+
+    def __init__(self, *args, **kwargs):
+        adapter = kwargs.pop('adapter')
+        super(AdapterWrapper, self).__init__(*args, **kwargs)
+        self._self_surrogate = _AdapterFunctionSurrogate(
+                self.__wrapped__, adapter)
+        self._self_adapter = adapter
+
+    @property
+    def __code__(self):
+        return self._self_surrogate.__code__
+
+    @property
+    def __defaults__(self):
+        return self._self_surrogate.__defaults__
+
+    @property
+    def __kwdefaults__(self):
+        return self._self_surrogate.__kwdefaults__
+
+    if PY2:
+        func_code = __code__
+        func_defaults = __defaults__
+
+    @property
+    def __signature__(self):
+        return self._self_surrogate.__signature__
+
+class AdapterFactory(object):
+    def __call__(self, wrapped):
+        raise NotImplementedError()
+
+class DelegatedAdapterFactory(AdapterFactory):
+    def __init__(self, factory):
+        super(DelegatedAdapterFactory, self).__init__()
+        self.factory = factory
+    def __call__(self, wrapped):
+        return self.factory(wrapped)
+
+adapter_factory = DelegatedAdapterFactory
+
+# Decorator for creating other decorators. This decorator and the
+# wrappers which they use are designed to properly preserve any name
+# attributes, function signatures etc, in addition to the wrappers
+# themselves acting like a transparent proxy for the original wrapped
+# function so the wrapper is effectively indistinguishable from the
+# original wrapped function.
+
+def decorator(wrapper=None, enabled=None, adapter=None, proxy=FunctionWrapper):
+    # The decorator should be supplied with a single positional argument
+    # which is the wrapper function to be used to implement the
+    # decorator. This may be preceded by a step whereby the keyword
+    # arguments are supplied to customise the behaviour of the
+    # decorator. The 'adapter' argument is used to optionally denote a
+    # separate function which is notionally used by an adapter
+    # decorator. In that case parts of the function '__code__' and
+    # '__defaults__' attributes are used from the adapter function
+    # rather than those of the wrapped function. This allows for the
+    # argument specification from inspect.getfullargspec() and similar
+    # functions to be overridden with a prototype for a different
+    # function than what was wrapped. The 'enabled' argument provides a
+    # way to enable/disable the use of the decorator. If the type of
+    # 'enabled' is a boolean, then it is evaluated immediately and the
+    # wrapper not even applied if it is False. If not a boolean, it will
+    # be evaluated when the wrapper is called for an unbound wrapper,
+    # and when binding occurs for a bound wrapper. When being evaluated,
+    # if 'enabled' is callable it will be called to obtain the value to
+    # be checked. If False, the wrapper will not be called and instead
+    # the original wrapped function will be called directly instead.
+    # The 'proxy' argument provides a way of passing a custom version of
+    # the FunctionWrapper class used in decorating the function.
+
+    if wrapper is not None:
+        # Helper function for creating wrapper of the appropriate
+        # time when we need it down below.
+
+        def _build(wrapped, wrapper, enabled=None, adapter=None):
+            if adapter:
+                if isinstance(adapter, AdapterFactory):
+                    adapter = adapter(wrapped)
+
+                if not callable(adapter):
+                    ns = {}
+
+                    # Check if the signature argument specification has
+                    # annotations. If it does then we need to remember
+                    # it but also drop it when attempting to manufacture
+                    # a standin adapter function. This is necessary else
+                    # it will try and look up any types referenced in
+                    # the annotations in the empty namespace we use,
+                    # which will fail.
+
+                    annotations = {}
+
+                    if not isinstance(adapter, string_types):
+                        if len(adapter) == 7:
+                            annotations = adapter[-1]
+                            adapter = adapter[:-1]
+                        adapter = formatargspec(*adapter)
+
+                    exec_('def adapter{}: pass'.format(adapter), ns, ns)
+                    adapter = ns['adapter']
+
+                    # Override the annotations for the manufactured
+                    # adapter function so they match the original
+                    # adapter signature argument specification.
+
+                    if annotations:
+                        adapter.__annotations__ = annotations
+
+                return AdapterWrapper(wrapped=wrapped, wrapper=wrapper,
+                        enabled=enabled, adapter=adapter)
+
+            return proxy(wrapped=wrapped, wrapper=wrapper, enabled=enabled)
+
+        # The wrapper has been provided so return the final decorator.
+        # The decorator is itself one of our function wrappers so we
+        # can determine when it is applied to functions, instance methods
+        # or class methods. This allows us to bind the instance or class
+        # method so the appropriate self or cls attribute is supplied
+        # when it is finally called.
+
+        def _wrapper(wrapped, instance, args, kwargs):
+            # We first check for the case where the decorator was applied
+            # to a class type.
+            #
+            #     @decorator
+            #     class mydecoratorclass(object):
+            #         def __init__(self, arg=None):
+            #             self.arg = arg
+            #         def __call__(self, wrapped, instance, args, kwargs):
+            #             return wrapped(*args, **kwargs)
+            #
+            #     @mydecoratorclass(arg=1)
+            #     def function():
+            #         pass
+            #
+            # In this case an instance of the class is to be used as the
+            # decorator wrapper function. If args was empty at this point,
+            # then it means that there were optional keyword arguments
+            # supplied to be used when creating an instance of the class
+            # to be used as the wrapper function.
+
+            if instance is None and isclass(wrapped) and not args:
+                # We still need to be passed the target function to be
+                # wrapped as yet, so we need to return a further function
+                # to be able to capture it.
+
+                def _capture(target_wrapped):
+                    # Now have the target function to be wrapped and need
+                    # to create an instance of the class which is to act
+                    # as the decorator wrapper function. Before we do that,
+                    # we need to first check that use of the decorator
+                    # hadn't been disabled by a simple boolean. If it was,
+                    # the target function to be wrapped is returned instead.
+
+                    _enabled = enabled
+                    if type(_enabled) is bool:
+                        if not _enabled:
+                            return target_wrapped
+                        _enabled = None
+
+                    # Now create an instance of the class which is to act
+                    # as the decorator wrapper function. Any arguments had
+                    # to be supplied as keyword only arguments so that is
+                    # all we pass when creating it.
+
+                    target_wrapper = wrapped(**kwargs)
+
+                    # Finally build the wrapper itself and return it.
+
+                    return _build(target_wrapped, target_wrapper,
+                            _enabled, adapter)
+
+                return _capture
+
+            # We should always have the target function to be wrapped at
+            # this point as the first (and only) value in args.
+
+            target_wrapped = args[0]
+
+            # Need to now check that use of the decorator hadn't been
+            # disabled by a simple boolean. If it was, then target
+            # function to be wrapped is returned instead.
+
+            _enabled = enabled
+            if type(_enabled) is bool:
+                if not _enabled:
+                    return target_wrapped
+                _enabled = None
+
+            # We now need to build the wrapper, but there are a couple of
+            # different cases we need to consider.
+
+            if instance is None:
+                if isclass(wrapped):
+                    # In this case the decorator was applied to a class
+                    # type but optional keyword arguments were not supplied
+                    # for initialising an instance of the class to be used
+                    # as the decorator wrapper function.
+                    #
+                    #     @decorator
+                    #     class mydecoratorclass(object):
+                    #         def __init__(self, arg=None):
+                    #             self.arg = arg
+                    #         def __call__(self, wrapped, instance,
+                    #                 args, kwargs):
+                    #             return wrapped(*args, **kwargs)
+                    #
+                    #     @mydecoratorclass
+                    #     def function():
+                    #         pass
+                    #
+                    # We still need to create an instance of the class to
+                    # be used as the decorator wrapper function, but no
+                    # arguments are pass.
+
+                    target_wrapper = wrapped()
+
+                else:
+                    # In this case the decorator was applied to a normal
+                    # function, or possibly a static method of a class.
+                    #
+                    #     @decorator
+                    #     def mydecoratorfuntion(wrapped, instance,
+                    #             args, kwargs):
+                    #         return wrapped(*args, **kwargs)
+                    #
+                    #     @mydecoratorfunction
+                    #     def function():
+                    #         pass
+                    #
+                    # That normal function becomes the decorator wrapper
+                    # function.
+
+                    target_wrapper = wrapper
+
+            else:
+                if isclass(instance):
+                    # In this case the decorator was applied to a class
+                    # method.
+                    #
+                    #     class myclass(object):
+                    #         @decorator
+                    #         @classmethod
+                    #         def decoratorclassmethod(cls, wrapped,
+                    #                 instance, args, kwargs):
+                    #             return wrapped(*args, **kwargs)
+                    #
+                    #     instance = myclass()
+                    #
+                    #     @instance.decoratorclassmethod
+                    #     def function():
+                    #         pass
+                    #
+                    # This one is a bit strange because binding was actually
+                    # performed on the wrapper created by our decorator
+                    # factory. We need to apply that binding to the decorator
+                    # wrapper function that the decorator factory
+                    # was applied to.
+
+                    target_wrapper = wrapper.__get__(None, instance)
+
+                else:
+                    # In this case the decorator was applied to an instance
+                    # method.
+                    #
+                    #     class myclass(object):
+                    #         @decorator
+                    #         def decoratorclassmethod(self, wrapped,
+                    #                 instance, args, kwargs):
+                    #             return wrapped(*args, **kwargs)
+                    #
+                    #     instance = myclass()
+                    #
+                    #     @instance.decoratorclassmethod
+                    #     def function():
+                    #         pass
+                    #
+                    # This one is a bit strange because binding was actually
+                    # performed on the wrapper created by our decorator
+                    # factory. We need to apply that binding to the decorator
+                    # wrapper function that the decorator factory
+                    # was applied to.
+
+                    target_wrapper = wrapper.__get__(instance, type(instance))
+
+            # Finally build the wrapper itself and return it.
+
+            return _build(target_wrapped, target_wrapper, _enabled, adapter)
+
+        # We first return our magic function wrapper here so we can
+        # determine in what context the decorator factory was used. In
+        # other words, it is itself a universal decorator. The decorator
+        # function is used as the adapter so that linters see a signature
+        # corresponding to the decorator and not the wrapper it is being
+        # applied to.
+
+        return _build(wrapper, _wrapper, adapter=decorator)
+
+    else:
+        # The wrapper still has not been provided, so we are just
+        # collecting the optional keyword arguments. Return the
+        # decorator again wrapped in a partial using the collected
+        # arguments.
+
+        return partial(decorator, enabled=enabled, adapter=adapter,
+                proxy=proxy)
+
+# Decorator for implementing thread synchronization. It can be used as a
+# decorator, in which case the synchronization context is determined by
+# what type of function is wrapped, or it can also be used as a context
+# manager, where the user needs to supply the correct synchronization
+# context. It is also possible to supply an object which appears to be a
+# synchronization primitive of some sort, by virtue of having release()
+# and acquire() methods. In that case that will be used directly as the
+# synchronization primitive without creating a separate lock against the
+# derived or supplied context.
+
+def synchronized(wrapped):
+    # Determine if being passed an object which is a synchronization
+    # primitive. We can't check by type for Lock, RLock, Semaphore etc,
+    # as the means of creating them isn't the type. Therefore use the
+    # existence of acquire() and release() methods. This is more
+    # extensible anyway as it allows custom synchronization mechanisms.
+
+    if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'):
+        # We remember what the original lock is and then return a new
+        # decorator which accesses and locks it. When returning the new
+        # decorator we wrap it with an object proxy so we can override
+        # the context manager methods in case it is being used to wrap
+        # synchronized statements with a 'with' statement.
+
+        lock = wrapped
+
+        @decorator
+        def _synchronized(wrapped, instance, args, kwargs):
+            # Execute the wrapped function while the original supplied
+            # lock is held.
+
+            with lock:
+                return wrapped(*args, **kwargs)
+
+        class _PartialDecorator(CallableObjectProxy):
+
+            def __enter__(self):
+                lock.acquire()
+                return lock
+
+            def __exit__(self, *args):
+                lock.release()
+
+        return _PartialDecorator(wrapped=_synchronized)
+
+    # Following only apply when the lock is being created automatically
+    # based on the context of what was supplied. In this case we supply
+    # a final decorator, but need to use FunctionWrapper directly as we
+    # want to derive from it to add context manager methods in case it is
+    # being used to wrap synchronized statements with a 'with' statement.
+
+    def _synchronized_lock(context):
+        # Attempt to retrieve the lock for the specific context.
+
+        lock = vars(context).get('_synchronized_lock', None)
+
+        if lock is None:
+            # There is no existing lock defined for the context we
+            # are dealing with so we need to create one. This needs
+            # to be done in a way to guarantee there is only one
+            # created, even if multiple threads try and create it at
+            # the same time. We can't always use the setdefault()
+            # method on the __dict__ for the context. This is the
+            # case where the context is a class, as __dict__ is
+            # actually a dictproxy. What we therefore do is use a
+            # meta lock on this wrapper itself, to control the
+            # creation and assignment of the lock attribute against
+            # the context.
+
+            with synchronized._synchronized_meta_lock:
+                # We need to check again for whether the lock we want
+                # exists in case two threads were trying to create it
+                # at the same time and were competing to create the
+                # meta lock.
+
+                lock = vars(context).get('_synchronized_lock', None)
+
+                if lock is None:
+                    lock = RLock()
+                    setattr(context, '_synchronized_lock', lock)
+
+        return lock
+
+    def _synchronized_wrapper(wrapped, instance, args, kwargs):
+        # Execute the wrapped function while the lock for the
+        # desired context is held. If instance is None then the
+        # wrapped function is used as the context.
+
+        with _synchronized_lock(instance if instance is not None else wrapped):
+            return wrapped(*args, **kwargs)
+
+    class _FinalDecorator(FunctionWrapper):
+
+        def __enter__(self):
+            self._self_lock = _synchronized_lock(self.__wrapped__)
+            self._self_lock.acquire()
+            return self._self_lock
+
+        def __exit__(self, *args):
+            self._self_lock.release()
+
+    return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)
+
+synchronized._synchronized_meta_lock = Lock()
diff --git a/.venv/lib/python3.12/site-packages/wrapt/importer.py b/.venv/lib/python3.12/site-packages/wrapt/importer.py
new file mode 100644
index 00000000..23fcbd2f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/importer.py
@@ -0,0 +1,295 @@
+"""This module implements a post import hook mechanism styled after what is
+described in PEP-369. Note that it doesn't cope with modules being reloaded.
+
+"""
+
+import sys
+import threading
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    string_types = basestring,
+    find_spec = None
+else:
+    string_types = str,
+    from importlib.util import find_spec
+
+from .__wrapt__ import ObjectProxy
+
+# The dictionary registering any post import hooks to be triggered once
+# the target module has been imported. Once a module has been imported
+# and the hooks fired, the list of hooks recorded against the target
+# module will be truncated but the list left in the dictionary. This
+# acts as a flag to indicate that the module had already been imported.
+
+_post_import_hooks = {}
+_post_import_hooks_init = False
+_post_import_hooks_lock = threading.RLock()
+
+# Register a new post import hook for the target module name. This
+# differs from the PEP-369 implementation in that it also allows the
+# hook function to be specified as a string consisting of the name of
+# the callback in the form 'module:function'. This will result in a
+# proxy callback being registered which will defer loading of the
+# specified module containing the callback function until required.
+
+def _create_import_hook_from_string(name):
+    def import_hook(module):
+        module_name, function = name.split(':')
+        attrs = function.split('.')
+        __import__(module_name)
+        callback = sys.modules[module_name]
+        for attr in attrs:
+            callback = getattr(callback, attr)
+        return callback(module)
+    return import_hook
+
+def register_post_import_hook(hook, name):
+    # Create a deferred import hook if hook is a string name rather than
+    # a callable function.
+
+    if isinstance(hook, string_types):
+        hook = _create_import_hook_from_string(hook)
+
+    with _post_import_hooks_lock:
+        # Automatically install the import hook finder if it has not already
+        # been installed.
+
+        global _post_import_hooks_init
+
+        if not _post_import_hooks_init:
+            _post_import_hooks_init = True
+            sys.meta_path.insert(0, ImportHookFinder())
+
+        # Check if the module is already imported. If not, register the hook
+        # to be called after import.
+
+        module = sys.modules.get(name, None)
+
+        if module is None:
+            _post_import_hooks.setdefault(name, []).append(hook)
+
+    # If the module is already imported, we fire the hook right away. Note that
+    # the hook is called outside of the lock to avoid deadlocks if code run as a
+    # consequence of calling the module import hook in turn triggers a separate
+    # thread which tries to register an import hook.
+
+    if module is not None:
+        hook(module)
+
+# Register post import hooks defined as package entry points.
+
+def _create_import_hook_from_entrypoint(entrypoint):
+    def import_hook(module):
+        __import__(entrypoint.module_name)
+        callback = sys.modules[entrypoint.module_name]
+        for attr in entrypoint.attrs:
+            callback = getattr(callback, attr)
+        return callback(module)
+    return import_hook
+
+def discover_post_import_hooks(group):
+    try:
+        import pkg_resources
+    except ImportError:
+        return
+
+    for entrypoint in pkg_resources.iter_entry_points(group=group):
+        callback = _create_import_hook_from_entrypoint(entrypoint)
+        register_post_import_hook(callback, entrypoint.name)
+
+# Indicate that a module has been loaded. Any post import hooks which
+# were registered against the target module will be invoked. If an
+# exception is raised in any of the post import hooks, that will cause
+# the import of the target module to fail.
+
+def notify_module_loaded(module):
+    name = getattr(module, '__name__', None)
+
+    with _post_import_hooks_lock:
+        hooks = _post_import_hooks.pop(name, ())
+
+    # Note that the hook is called outside of the lock to avoid deadlocks if
+    # code run as a consequence of calling the module import hook in turn
+    # triggers a separate thread which tries to register an import hook.
+
+    for hook in hooks:
+        hook(module)
+
+# A custom module import finder. This intercepts attempts to import
+# modules and watches out for attempts to import target modules of
+# interest. When a module of interest is imported, then any post import
+# hooks which are registered will be invoked.
+
+class _ImportHookLoader:
+
+    def load_module(self, fullname):
+        module = sys.modules[fullname]
+        notify_module_loaded(module)
+
+        return module
+
+class _ImportHookChainedLoader(ObjectProxy):
+
+    def __init__(self, loader):
+        super(_ImportHookChainedLoader, self).__init__(loader)
+
+        if hasattr(loader, "load_module"):
+          self.__self_setattr__('load_module', self._self_load_module)
+        if hasattr(loader, "create_module"):
+          self.__self_setattr__('create_module', self._self_create_module)
+        if hasattr(loader, "exec_module"):
+          self.__self_setattr__('exec_module', self._self_exec_module)
+
+    def _self_set_loader(self, module):
+        # Set module's loader to self.__wrapped__ unless it's already set to
+        # something else. Import machinery will set it to spec.loader if it is
+        # None, so handle None as well. The module may not support attribute
+        # assignment, in which case we simply skip it. Note that we also deal
+        # with __loader__ not existing at all. This is to future proof things
+        # due to proposal to remove the attribue as described in the GitHub
+        # issue at https://github.com/python/cpython/issues/77458. Also prior
+        # to Python 3.3, the __loader__ attribute was only set if a custom
+        # module loader was used. It isn't clear whether the attribute still
+        # existed in that case or was set to None.
+
+        class UNDEFINED: pass
+
+        if getattr(module, "__loader__", UNDEFINED) in (None, self):
+            try:
+                module.__loader__ = self.__wrapped__
+            except AttributeError:
+                pass
+
+        if (getattr(module, "__spec__", None) is not None
+                and getattr(module.__spec__, "loader", None) is self):
+            module.__spec__.loader = self.__wrapped__
+
+    def _self_load_module(self, fullname):
+        module = self.__wrapped__.load_module(fullname)
+        self._self_set_loader(module)
+        notify_module_loaded(module)
+
+        return module
+
+    # Python 3.4 introduced create_module() and exec_module() instead of
+    # load_module() alone. Splitting the two steps.
+
+    def _self_create_module(self, spec):
+        return self.__wrapped__.create_module(spec)
+
+    def _self_exec_module(self, module):
+        self._self_set_loader(module)
+        self.__wrapped__.exec_module(module)
+        notify_module_loaded(module)
+
+class ImportHookFinder:
+
+    def __init__(self):
+        self.in_progress = {}
+
+    def find_module(self, fullname, path=None):
+        # If the module being imported is not one we have registered
+        # post import hooks for, we can return immediately. We will
+        # take no further part in the importing of this module.
+
+        with _post_import_hooks_lock:
+            if fullname not in _post_import_hooks:
+                return None
+
+        # When we are interested in a specific module, we will call back
+        # into the import system a second time to defer to the import
+        # finder that is supposed to handle the importing of the module.
+        # We set an in progress flag for the target module so that on
+        # the second time through we don't trigger another call back
+        # into the import system and cause a infinite loop.
+
+        if fullname in self.in_progress:
+            return None
+
+        self.in_progress[fullname] = True
+
+        # Now call back into the import system again.
+
+        try:
+            if not find_spec:
+                # For Python 2 we don't have much choice but to
+                # call back in to __import__(). This will
+                # actually cause the module to be imported. If no
+                # module could be found then ImportError will be
+                # raised. Otherwise we return a loader which
+                # returns the already loaded module and invokes
+                # the post import hooks.
+
+                __import__(fullname)
+
+                return _ImportHookLoader()
+
+            else:
+                # For Python 3 we need to use find_spec().loader
+                # from the importlib.util module. It doesn't actually
+                # import the target module and only finds the
+                # loader. If a loader is found, we need to return
+                # our own loader which will then in turn call the
+                # real loader to import the module and invoke the
+                # post import hooks.
+
+                loader = getattr(find_spec(fullname), "loader", None)
+
+                if loader and not isinstance(loader, _ImportHookChainedLoader):
+                    return _ImportHookChainedLoader(loader)
+
+        finally:
+            del self.in_progress[fullname]
+
+    def find_spec(self, fullname, path=None, target=None):
+        # Since Python 3.4, you are meant to implement find_spec() method
+        # instead of find_module() and since Python 3.10 you get deprecation
+        # warnings if you don't define find_spec().
+
+        # If the module being imported is not one we have registered
+        # post import hooks for, we can return immediately. We will
+        # take no further part in the importing of this module.
+
+        with _post_import_hooks_lock:
+            if fullname not in _post_import_hooks:
+                return None
+
+        # When we are interested in a specific module, we will call back
+        # into the import system a second time to defer to the import
+        # finder that is supposed to handle the importing of the module.
+        # We set an in progress flag for the target module so that on
+        # the second time through we don't trigger another call back
+        # into the import system and cause a infinite loop.
+
+        if fullname in self.in_progress:
+            return None
+
+        self.in_progress[fullname] = True
+
+        # Now call back into the import system again.
+
+        try:
+            # This should only be Python 3 so find_spec() should always
+            # exist so don't need to check.
+
+            spec = find_spec(fullname)
+            loader = getattr(spec, "loader", None)
+
+            if loader and not isinstance(loader, _ImportHookChainedLoader):
+                spec.loader = _ImportHookChainedLoader(loader)
+
+            return spec
+
+        finally:
+            del self.in_progress[fullname]
+
+# Decorator for marking that a function should be called as a post
+# import hook when the target module is imported.
+
+def when_imported(name):
+    def register(hook):
+        register_post_import_hook(hook, name)
+        return hook
+    return register
diff --git a/.venv/lib/python3.12/site-packages/wrapt/patches.py b/.venv/lib/python3.12/site-packages/wrapt/patches.py
new file mode 100644
index 00000000..e22adf7c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/patches.py
@@ -0,0 +1,141 @@
+import inspect
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    string_types = basestring,
+else:
+    string_types = str,
+
+from .__wrapt__ import FunctionWrapper
+
+# Helper functions for applying wrappers to existing functions.
+
+def resolve_path(module, name):
+    if isinstance(module, string_types):
+        __import__(module)
+        module = sys.modules[module]
+
+    parent = module
+
+    path = name.split('.')
+    attribute = path[0]
+
+    # We can't just always use getattr() because in doing
+    # that on a class it will cause binding to occur which
+    # will complicate things later and cause some things not
+    # to work. For the case of a class we therefore access
+    # the __dict__ directly. To cope though with the wrong
+    # class being given to us, or a method being moved into
+    # a base class, we need to walk the class hierarchy to
+    # work out exactly which __dict__ the method was defined
+    # in, as accessing it from __dict__ will fail if it was
+    # not actually on the class given. Fallback to using
+    # getattr() if we can't find it. If it truly doesn't
+    # exist, then that will fail.
+
+    def lookup_attribute(parent, attribute):
+        if inspect.isclass(parent):
+            for cls in inspect.getmro(parent):
+                if attribute in vars(cls):
+                    return vars(cls)[attribute]
+            else:
+                return getattr(parent, attribute)
+        else:
+            return getattr(parent, attribute)
+
+    original = lookup_attribute(parent, attribute)
+
+    for attribute in path[1:]:
+        parent = original
+        original = lookup_attribute(parent, attribute)
+
+    return (parent, attribute, original)
+
+def apply_patch(parent, attribute, replacement):
+    setattr(parent, attribute, replacement)
+
+def wrap_object(module, name, factory, args=(), kwargs={}):
+    (parent, attribute, original) = resolve_path(module, name)
+    wrapper = factory(original, *args, **kwargs)
+    apply_patch(parent, attribute, wrapper)
+    return wrapper
+
+# Function for applying a proxy object to an attribute of a class
+# instance. The wrapper works by defining an attribute of the same name
+# on the class which is a descriptor and which intercepts access to the
+# instance attribute. Note that this cannot be used on attributes which
+# are themselves defined by a property object.
+
+class AttributeWrapper(object):
+
+    def __init__(self, attribute, factory, args, kwargs):
+        self.attribute = attribute
+        self.factory = factory
+        self.args = args
+        self.kwargs = kwargs
+
+    def __get__(self, instance, owner):
+        value = instance.__dict__[self.attribute]
+        return self.factory(value, *self.args, **self.kwargs)
+
+    def __set__(self, instance, value):
+        instance.__dict__[self.attribute] = value
+
+    def __delete__(self, instance):
+        del instance.__dict__[self.attribute]
+
+def wrap_object_attribute(module, name, factory, args=(), kwargs={}):
+    path, attribute = name.rsplit('.', 1)
+    parent = resolve_path(module, path)[2]
+    wrapper = AttributeWrapper(attribute, factory, args, kwargs)
+    apply_patch(parent, attribute, wrapper)
+    return wrapper
+
+# Functions for creating a simple decorator using a FunctionWrapper,
+# plus short cut functions for applying wrappers to functions. These are
+# for use when doing monkey patching. For a more featured way of
+# creating decorators see the decorator decorator instead.
+
+def function_wrapper(wrapper):
+    def _wrapper(wrapped, instance, args, kwargs):
+        target_wrapped = args[0]
+        if instance is None:
+            target_wrapper = wrapper
+        elif inspect.isclass(instance):
+            target_wrapper = wrapper.__get__(None, instance)
+        else:
+            target_wrapper = wrapper.__get__(instance, type(instance))
+        return FunctionWrapper(target_wrapped, target_wrapper)
+    return FunctionWrapper(wrapper, _wrapper)
+
+def wrap_function_wrapper(module, name, wrapper):
+    return wrap_object(module, name, FunctionWrapper, (wrapper,))
+
+def patch_function_wrapper(module, name, enabled=None):
+    def _wrapper(wrapper):
+        return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))
+    return _wrapper
+
+def transient_function_wrapper(module, name):
+    def _decorator(wrapper):
+        def _wrapper(wrapped, instance, args, kwargs):
+            target_wrapped = args[0]
+            if instance is None:
+                target_wrapper = wrapper
+            elif inspect.isclass(instance):
+                target_wrapper = wrapper.__get__(None, instance)
+            else:
+                target_wrapper = wrapper.__get__(instance, type(instance))
+            def _execute(wrapped, instance, args, kwargs):
+                (parent, attribute, original) = resolve_path(module, name)
+                replacement = FunctionWrapper(original, target_wrapper)
+                setattr(parent, attribute, replacement)
+                try:
+                    return wrapped(*args, **kwargs)
+                finally:
+                    setattr(parent, attribute, original)
+            return FunctionWrapper(target_wrapped, _execute)
+        return FunctionWrapper(wrapper, _wrapper)
+    return _decorator
diff --git a/.venv/lib/python3.12/site-packages/wrapt/weakrefs.py b/.venv/lib/python3.12/site-packages/wrapt/weakrefs.py
new file mode 100644
index 00000000..f931b60d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/weakrefs.py
@@ -0,0 +1,98 @@
+import functools
+import weakref
+
+from .__wrapt__ import ObjectProxy, _FunctionWrapperBase
+
+# A weak function proxy. This will work on instance methods, class
+# methods, static methods and regular functions. Special treatment is
+# needed for the method types because the bound method is effectively a
+# transient object and applying a weak reference to one will immediately
+# result in it being destroyed and the weakref callback called. The weak
+# reference is therefore applied to the instance the method is bound to
+# and the original function. The function is then rebound at the point
+# of a call via the weak function proxy.
+
+def _weak_function_proxy_callback(ref, proxy, callback):
+    if proxy._self_expired:
+        return
+
+    proxy._self_expired = True
+
+    # This could raise an exception. We let it propagate back and let
+    # the weakref.proxy() deal with it, at which point it generally
+    # prints out a short error message direct to stderr and keeps going.
+
+    if callback is not None:
+        callback(proxy)
+
+class WeakFunctionProxy(ObjectProxy):
+
+    __slots__ = ('_self_expired', '_self_instance')
+
+    def __init__(self, wrapped, callback=None):
+        # We need to determine if the wrapped function is actually a
+        # bound method. In the case of a bound method, we need to keep a
+        # reference to the original unbound function and the instance.
+        # This is necessary because if we hold a reference to the bound
+        # function, it will be the only reference and given it is a
+        # temporary object, it will almost immediately expire and
+        # the weakref callback triggered. So what is done is that we
+        # hold a reference to the instance and unbound function and
+        # when called bind the function to the instance once again and
+        # then call it. Note that we avoid using a nested function for
+        # the callback here so as not to cause any odd reference cycles.
+
+        _callback = callback and functools.partial(
+                _weak_function_proxy_callback, proxy=self,
+                callback=callback)
+
+        self._self_expired = False
+
+        if isinstance(wrapped, _FunctionWrapperBase):
+            self._self_instance = weakref.ref(wrapped._self_instance,
+                    _callback)
+
+            if wrapped._self_parent is not None:
+                super(WeakFunctionProxy, self).__init__(
+                        weakref.proxy(wrapped._self_parent, _callback))
+
+            else:
+                super(WeakFunctionProxy, self).__init__(
+                        weakref.proxy(wrapped, _callback))
+
+            return
+
+        try:
+            self._self_instance = weakref.ref(wrapped.__self__, _callback)
+
+            super(WeakFunctionProxy, self).__init__(
+                    weakref.proxy(wrapped.__func__, _callback))
+
+        except AttributeError:
+            self._self_instance = None
+
+            super(WeakFunctionProxy, self).__init__(
+                    weakref.proxy(wrapped, _callback))
+
+    def __call__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+
+        # We perform a boolean check here on the instance and wrapped
+        # function as that will trigger the reference error prior to
+        # calling if the reference had expired.
+
+        instance = self._self_instance and self._self_instance()
+        function = self.__wrapped__ and self.__wrapped__
+
+        # If the wrapped function was originally a bound function, for
+        # which we retained a reference to the instance and the unbound
+        # function we need to rebind the function and then call it. If
+        # not just called the wrapped function.
+
+        if instance is None:
+            return self.__wrapped__(*args, **kwargs)
+
+        return function.__get__(instance, type(instance))(*args, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/wrapt/wrappers.py b/.venv/lib/python3.12/site-packages/wrapt/wrappers.py
new file mode 100644
index 00000000..62da8a30
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/wrapt/wrappers.py
@@ -0,0 +1,821 @@
+import sys
+import operator
+import inspect
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    string_types = basestring,
+else:
+    string_types = str,
+
+def with_metaclass(meta, *bases):
+    """Create a base class with a metaclass."""
+    return meta("NewBase", bases, {})
+
+class _ObjectProxyMethods(object):
+
+    # We use properties to override the values of __module__ and
+    # __doc__. If we add these in ObjectProxy, the derived class
+    # __dict__ will still be setup to have string variants of these
+    # attributes and the rules of descriptors means that they appear to
+    # take precedence over the properties in the base class. To avoid
+    # that, we copy the properties into the derived class type itself
+    # via a meta class. In that way the properties will always take
+    # precedence.
+
+    @property
+    def __module__(self):
+        return self.__wrapped__.__module__
+
+    @__module__.setter
+    def __module__(self, value):
+        self.__wrapped__.__module__ = value
+
+    @property
+    def __doc__(self):
+        return self.__wrapped__.__doc__
+
+    @__doc__.setter
+    def __doc__(self, value):
+        self.__wrapped__.__doc__ = value
+
+    # We similar use a property for __dict__. We need __dict__ to be
+    # explicit to ensure that vars() works as expected.
+
+    @property
+    def __dict__(self):
+        return self.__wrapped__.__dict__
+
+    # Need to also propagate the special __weakref__ attribute for case
+    # where decorating classes which will define this. If do not define
+    # it and use a function like inspect.getmembers() on a decorator
+    # class it will fail. This can't be in the derived classes.
+
+    @property
+    def __weakref__(self):
+        return self.__wrapped__.__weakref__
+
+class _ObjectProxyMetaType(type):
+    def __new__(cls, name, bases, dictionary):
+        # Copy our special properties into the class so that they
+        # always take precedence over attributes of the same name added
+        # during construction of a derived class. This is to save
+        # duplicating the implementation for them in all derived classes.
+
+        dictionary.update(vars(_ObjectProxyMethods))
+
+        return type.__new__(cls, name, bases, dictionary)
+
+class ObjectProxy(with_metaclass(_ObjectProxyMetaType)):
+
+    __slots__ = '__wrapped__'
+
+    def __init__(self, wrapped):
+        object.__setattr__(self, '__wrapped__', wrapped)
+
+        # Python 3.2+ has the __qualname__ attribute, but it does not
+        # allow it to be overridden using a property and it must instead
+        # be an actual string object instead.
+
+        try:
+            object.__setattr__(self, '__qualname__', wrapped.__qualname__)
+        except AttributeError:
+            pass
+
+        # Python 3.10 onwards also does not allow itself to be overridden
+        # using a property and it must instead be set explicitly.
+
+        try:
+            object.__setattr__(self, '__annotations__', wrapped.__annotations__)
+        except AttributeError:
+            pass
+
+    def __self_setattr__(self, name, value):
+        object.__setattr__(self, name, value)
+
+    @property
+    def __name__(self):
+        return self.__wrapped__.__name__
+
+    @__name__.setter
+    def __name__(self, value):
+        self.__wrapped__.__name__ = value
+
+    @property
+    def __class__(self):
+        return self.__wrapped__.__class__
+
+    @__class__.setter
+    def __class__(self, value):
+        self.__wrapped__.__class__ = value
+
+    def __dir__(self):
+        return dir(self.__wrapped__)
+
+    def __str__(self):
+        return str(self.__wrapped__)
+
+    if not PY2:
+        def __bytes__(self):
+            return bytes(self.__wrapped__)
+
+    def __repr__(self):
+        return '<{} at 0x{:x} for {} at 0x{:x}>'.format(
+                type(self).__name__, id(self),
+                type(self.__wrapped__).__name__,
+                id(self.__wrapped__))
+
+    def __format__(self, format_spec):
+        return format(self.__wrapped__, format_spec)
+
+    def __reversed__(self):
+        return reversed(self.__wrapped__)
+
+    if not PY2:
+        def __round__(self, ndigits=None):
+            return round(self.__wrapped__, ndigits)
+
+    if sys.hexversion >= 0x03070000:
+        def __mro_entries__(self, bases):
+            return (self.__wrapped__,)
+
+    def __lt__(self, other):
+        return self.__wrapped__ < other
+
+    def __le__(self, other):
+        return self.__wrapped__ <= other
+
+    def __eq__(self, other):
+        return self.__wrapped__ == other
+
+    def __ne__(self, other):
+        return self.__wrapped__ != other
+
+    def __gt__(self, other):
+        return self.__wrapped__ > other
+
+    def __ge__(self, other):
+        return self.__wrapped__ >= other
+
+    def __hash__(self):
+        return hash(self.__wrapped__)
+
+    def __nonzero__(self):
+        return bool(self.__wrapped__)
+
+    def __bool__(self):
+        return bool(self.__wrapped__)
+
+    def __setattr__(self, name, value):
+        if name.startswith('_self_'):
+            object.__setattr__(self, name, value)
+
+        elif name == '__wrapped__':
+            object.__setattr__(self, name, value)
+            try:
+                object.__delattr__(self, '__qualname__')
+            except AttributeError:
+                pass
+            try:
+                object.__setattr__(self, '__qualname__', value.__qualname__)
+            except AttributeError:
+                pass
+            try:
+                object.__delattr__(self, '__annotations__')
+            except AttributeError:
+                pass
+            try:
+                object.__setattr__(self, '__annotations__', value.__annotations__)
+            except AttributeError:
+                pass
+
+        elif name == '__qualname__':
+            setattr(self.__wrapped__, name, value)
+            object.__setattr__(self, name, value)
+
+        elif name == '__annotations__':
+            setattr(self.__wrapped__, name, value)
+            object.__setattr__(self, name, value)
+
+        elif hasattr(type(self), name):
+            object.__setattr__(self, name, value)
+
+        else:
+            setattr(self.__wrapped__, name, value)
+
+    def __getattr__(self, name):
+        # If we are being to lookup '__wrapped__' then the
+        # '__init__()' method cannot have been called.
+
+        if name == '__wrapped__':
+            raise ValueError('wrapper has not been initialised')
+
+        return getattr(self.__wrapped__, name)
+
+    def __delattr__(self, name):
+        if name.startswith('_self_'):
+            object.__delattr__(self, name)
+
+        elif name == '__wrapped__':
+            raise TypeError('__wrapped__ must be an object')
+
+        elif name == '__qualname__':
+            object.__delattr__(self, name)
+            delattr(self.__wrapped__, name)
+
+        elif hasattr(type(self), name):
+            object.__delattr__(self, name)
+
+        else:
+            delattr(self.__wrapped__, name)
+
+    def __add__(self, other):
+        return self.__wrapped__ + other
+
+    def __sub__(self, other):
+        return self.__wrapped__ - other
+
+    def __mul__(self, other):
+        return self.__wrapped__ * other
+
+    def __div__(self, other):
+        return operator.div(self.__wrapped__, other)
+
+    def __truediv__(self, other):
+        return operator.truediv(self.__wrapped__, other)
+
+    def __floordiv__(self, other):
+        return self.__wrapped__ // other
+
+    def __mod__(self, other):
+        return self.__wrapped__ % other
+
+    def __divmod__(self, other):
+        return divmod(self.__wrapped__, other)
+
+    def __pow__(self, other, *args):
+        return pow(self.__wrapped__, other, *args)
+
+    def __lshift__(self, other):
+        return self.__wrapped__ << other
+
+    def __rshift__(self, other):
+        return self.__wrapped__ >> other
+
+    def __and__(self, other):
+        return self.__wrapped__ & other
+
+    def __xor__(self, other):
+        return self.__wrapped__ ^ other
+
+    def __or__(self, other):
+        return self.__wrapped__ | other
+
+    def __radd__(self, other):
+        return other + self.__wrapped__
+
+    def __rsub__(self, other):
+        return other - self.__wrapped__
+
+    def __rmul__(self, other):
+        return other * self.__wrapped__
+
+    def __rdiv__(self, other):
+        return operator.div(other, self.__wrapped__)
+
+    def __rtruediv__(self, other):
+        return operator.truediv(other, self.__wrapped__)
+
+    def __rfloordiv__(self, other):
+        return other // self.__wrapped__
+
+    def __rmod__(self, other):
+        return other % self.__wrapped__
+
+    def __rdivmod__(self, other):
+        return divmod(other, self.__wrapped__)
+
+    def __rpow__(self, other, *args):
+        return pow(other, self.__wrapped__, *args)
+
+    def __rlshift__(self, other):
+        return other << self.__wrapped__
+
+    def __rrshift__(self, other):
+        return other >> self.__wrapped__
+
+    def __rand__(self, other):
+        return other & self.__wrapped__
+
+    def __rxor__(self, other):
+        return other ^ self.__wrapped__
+
+    def __ror__(self, other):
+        return other | self.__wrapped__
+
+    def __iadd__(self, other):
+        self.__wrapped__ += other
+        return self
+
+    def __isub__(self, other):
+        self.__wrapped__ -= other
+        return self
+
+    def __imul__(self, other):
+        self.__wrapped__ *= other
+        return self
+
+    def __idiv__(self, other):
+        self.__wrapped__ = operator.idiv(self.__wrapped__, other)
+        return self
+
+    def __itruediv__(self, other):
+        self.__wrapped__ = operator.itruediv(self.__wrapped__, other)
+        return self
+
+    def __ifloordiv__(self, other):
+        self.__wrapped__ //= other
+        return self
+
+    def __imod__(self, other):
+        self.__wrapped__ %= other
+        return self
+
+    def __ipow__(self, other):
+        self.__wrapped__ **= other
+        return self
+
+    def __ilshift__(self, other):
+        self.__wrapped__ <<= other
+        return self
+
+    def __irshift__(self, other):
+        self.__wrapped__ >>= other
+        return self
+
+    def __iand__(self, other):
+        self.__wrapped__ &= other
+        return self
+
+    def __ixor__(self, other):
+        self.__wrapped__ ^= other
+        return self
+
+    def __ior__(self, other):
+        self.__wrapped__ |= other
+        return self
+
+    def __neg__(self):
+        return -self.__wrapped__
+
+    def __pos__(self):
+        return +self.__wrapped__
+
+    def __abs__(self):
+        return abs(self.__wrapped__)
+
+    def __invert__(self):
+        return ~self.__wrapped__
+
+    def __int__(self):
+        return int(self.__wrapped__)
+
+    def __long__(self):
+        return long(self.__wrapped__)
+
+    def __float__(self):
+        return float(self.__wrapped__)
+
+    def __complex__(self):
+        return complex(self.__wrapped__)
+
+    def __oct__(self):
+        return oct(self.__wrapped__)
+
+    def __hex__(self):
+        return hex(self.__wrapped__)
+
+    def __index__(self):
+        return operator.index(self.__wrapped__)
+
+    def __len__(self):
+        return len(self.__wrapped__)
+
+    def __contains__(self, value):
+        return value in self.__wrapped__
+
+    def __getitem__(self, key):
+        return self.__wrapped__[key]
+
+    def __setitem__(self, key, value):
+        self.__wrapped__[key] = value
+
+    def __delitem__(self, key):
+        del self.__wrapped__[key]
+
+    def __getslice__(self, i, j):
+        return self.__wrapped__[i:j]
+
+    def __setslice__(self, i, j, value):
+        self.__wrapped__[i:j] = value
+
+    def __delslice__(self, i, j):
+        del self.__wrapped__[i:j]
+
+    def __enter__(self):
+        return self.__wrapped__.__enter__()
+
+    def __exit__(self, *args, **kwargs):
+        return self.__wrapped__.__exit__(*args, **kwargs)
+
+    def __iter__(self):
+        return iter(self.__wrapped__)
+
+    def __copy__(self):
+        raise NotImplementedError('object proxy must define __copy__()')
+
+    def __deepcopy__(self, memo):
+        raise NotImplementedError('object proxy must define __deepcopy__()')
+
+    def __reduce__(self):
+        raise NotImplementedError(
+                'object proxy must define __reduce__()')
+
+    def __reduce_ex__(self, protocol):
+        raise NotImplementedError(
+                'object proxy must define __reduce_ex__()')
+
+class CallableObjectProxy(ObjectProxy):
+
+    def __call__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+
+        return self.__wrapped__(*args, **kwargs)
+
+class PartialCallableObjectProxy(ObjectProxy):
+
+    def __init__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+
+        if len(args) < 1:
+            raise TypeError('partial type takes at least one argument')
+
+        wrapped, args = args[0], args[1:]
+
+        if not callable(wrapped):
+            raise TypeError('the first argument must be callable')
+
+        super(PartialCallableObjectProxy, self).__init__(wrapped)
+
+        self._self_args = args
+        self._self_kwargs = kwargs
+
+    def __call__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+    
+        _args = self._self_args + args
+
+        _kwargs = dict(self._self_kwargs)
+        _kwargs.update(kwargs)
+
+        return self.__wrapped__(*_args, **_kwargs)
+
+class _FunctionWrapperBase(ObjectProxy):
+
+    __slots__ = ('_self_instance', '_self_wrapper', '_self_enabled',
+            '_self_binding', '_self_parent', '_self_owner')
+
+    def __init__(self, wrapped, instance, wrapper, enabled=None,
+            binding='callable', parent=None, owner=None):
+
+        super(_FunctionWrapperBase, self).__init__(wrapped)
+
+        object.__setattr__(self, '_self_instance', instance)
+        object.__setattr__(self, '_self_wrapper', wrapper)
+        object.__setattr__(self, '_self_enabled', enabled)
+        object.__setattr__(self, '_self_binding', binding)
+        object.__setattr__(self, '_self_parent', parent)
+        object.__setattr__(self, '_self_owner', owner)
+
+    def __get__(self, instance, owner):
+        # This method is actually doing double duty for both unbound and bound
+        # derived wrapper classes. It should possibly be broken up and the
+        # distinct functionality moved into the derived classes. Can't do that
+        # straight away due to some legacy code which is relying on it being
+        # here in this base class.
+        #
+        # The distinguishing attribute which determines whether we are being
+        # called in an unbound or bound wrapper is the parent attribute. If
+        # binding has never occurred, then the parent will be None.
+        #
+        # First therefore, is if we are called in an unbound wrapper. In this
+        # case we perform the binding.
+        #
+        # We have two special cases to worry about here. These are where we are
+        # decorating a class or builtin function as neither provide a __get__()
+        # method to call. In this case we simply return self.
+        #
+        # Note that we otherwise still do binding even if instance is None and
+        # accessing an unbound instance method from a class. This is because we
+        # need to be able to later detect that specific case as we will need to
+        # extract the instance from the first argument of those passed in.
+
+        if self._self_parent is None:
+            # Technically can probably just check for existence of __get__ on
+            # the wrapped object, but this is more explicit.
+
+            if self._self_binding == 'builtin':
+                return self
+            
+            if self._self_binding == "class":
+                return self
+
+            binder = getattr(self.__wrapped__, '__get__', None)
+
+            if binder is None:
+                return self
+
+            descriptor =  binder(instance, owner)
+
+            return self.__bound_function_wrapper__(descriptor, instance,
+                    self._self_wrapper, self._self_enabled,
+                    self._self_binding, self, owner)
+
+        # Now we have the case of binding occurring a second time on what was
+        # already a bound function. In this case we would usually return
+        # ourselves again. This mirrors what Python does.
+        #
+        # The special case this time is where we were originally bound with an
+        # instance of None and we were likely an instance method. In that case
+        # we rebind against the original wrapped function from the parent again.
+
+        if self._self_instance is None and self._self_binding in ('function', 'instancemethod', 'callable'):
+            descriptor = self._self_parent.__wrapped__.__get__(
+                    instance, owner)
+
+            return self._self_parent.__bound_function_wrapper__(
+                    descriptor, instance, self._self_wrapper,
+                    self._self_enabled, self._self_binding,
+                    self._self_parent, owner)
+
+        return self
+
+    def __call__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+
+        # If enabled has been specified, then evaluate it at this point
+        # and if the wrapper is not to be executed, then simply return
+        # the bound function rather than a bound wrapper for the bound
+        # function. When evaluating enabled, if it is callable we call
+        # it, otherwise we evaluate it as a boolean.
+
+        if self._self_enabled is not None:
+            if callable(self._self_enabled):
+                if not self._self_enabled():
+                    return self.__wrapped__(*args, **kwargs)
+            elif not self._self_enabled:
+                return self.__wrapped__(*args, **kwargs)
+
+        # This can occur where initial function wrapper was applied to
+        # a function that was already bound to an instance. In that case
+        # we want to extract the instance from the function and use it.
+
+        if self._self_binding in ('function', 'instancemethod', 'classmethod', 'callable'):
+            if self._self_instance is None:
+                instance = getattr(self.__wrapped__, '__self__', None)
+                if instance is not None:
+                    return self._self_wrapper(self.__wrapped__, instance,
+                            args, kwargs)
+
+        # This is generally invoked when the wrapped function is being
+        # called as a normal function and is not bound to a class as an
+        # instance method. This is also invoked in the case where the
+        # wrapped function was a method, but this wrapper was in turn
+        # wrapped using the staticmethod decorator.
+
+        return self._self_wrapper(self.__wrapped__, self._self_instance,
+                args, kwargs)
+
+    def __set_name__(self, owner, name):
+        # This is a special method use to supply information to
+        # descriptors about what the name of variable in a class
+        # definition is. Not wanting to add this to ObjectProxy as not
+        # sure of broader implications of doing that. Thus restrict to
+        # FunctionWrapper used by decorators.
+
+        if hasattr(self.__wrapped__, "__set_name__"):
+            self.__wrapped__.__set_name__(owner, name)
+
+    def __instancecheck__(self, instance):
+        # This is a special method used by isinstance() to make checks
+        # instance of the `__wrapped__`.
+        return isinstance(instance, self.__wrapped__)
+
+    def __subclasscheck__(self, subclass):
+        # This is a special method used by issubclass() to make checks
+        # about inheritance of classes. We need to upwrap any object
+        # proxy. Not wanting to add this to ObjectProxy as not sure of
+        # broader implications of doing that. Thus restrict to
+        # FunctionWrapper used by decorators.
+
+        if hasattr(subclass, "__wrapped__"):
+            return issubclass(subclass.__wrapped__, self.__wrapped__)
+        else:
+            return issubclass(subclass, self.__wrapped__)
+
+class BoundFunctionWrapper(_FunctionWrapperBase):
+
+    def __call__(*args, **kwargs):
+        def _unpack_self(self, *args):
+            return self, args
+
+        self, args = _unpack_self(*args)
+
+        # If enabled has been specified, then evaluate it at this point and if
+        # the wrapper is not to be executed, then simply return the bound
+        # function rather than a bound wrapper for the bound function. When
+        # evaluating enabled, if it is callable we call it, otherwise we
+        # evaluate it as a boolean.
+
+        if self._self_enabled is not None:
+            if callable(self._self_enabled):
+                if not self._self_enabled():
+                    return self.__wrapped__(*args, **kwargs)
+            elif not self._self_enabled:
+                return self.__wrapped__(*args, **kwargs)
+
+        # We need to do things different depending on whether we are likely
+        # wrapping an instance method vs a static method or class method.
+
+        if self._self_binding == 'function':
+            if self._self_instance is None and args:
+                instance, newargs = args[0], args[1:]
+                if isinstance(instance, self._self_owner):
+                    wrapped = PartialCallableObjectProxy(self.__wrapped__, instance)
+                    return self._self_wrapper(wrapped, instance, newargs, kwargs)
+
+            return self._self_wrapper(self.__wrapped__, self._self_instance,
+                    args, kwargs)
+
+        elif self._self_binding == 'callable':
+            if self._self_instance is None:
+                # This situation can occur where someone is calling the
+                # instancemethod via the class type and passing the instance as
+                # the first argument. We need to shift the args before making
+                # the call to the wrapper and effectively bind the instance to
+                # the wrapped function using a partial so the wrapper doesn't
+                # see anything as being different.
+
+                if not args:
+                    raise TypeError('missing 1 required positional argument')
+
+                instance, args = args[0], args[1:]
+                wrapped = PartialCallableObjectProxy(self.__wrapped__, instance)
+                return self._self_wrapper(wrapped, instance, args, kwargs)
+
+            return self._self_wrapper(self.__wrapped__, self._self_instance,
+                    args, kwargs)
+
+        else:
+            # As in this case we would be dealing with a classmethod or
+            # staticmethod, then _self_instance will only tell us whether
+            # when calling the classmethod or staticmethod they did it via an
+            # instance of the class it is bound to and not the case where
+            # done by the class type itself. We thus ignore _self_instance
+            # and use the __self__ attribute of the bound function instead.
+            # For a classmethod, this means instance will be the class type
+            # and for a staticmethod it will be None. This is probably the
+            # more useful thing we can pass through even though we loose
+            # knowledge of whether they were called on the instance vs the
+            # class type, as it reflects what they have available in the
+            # decoratored function.
+
+            instance = getattr(self.__wrapped__, '__self__', None)
+
+            return self._self_wrapper(self.__wrapped__, instance, args,
+                    kwargs)
+
+class FunctionWrapper(_FunctionWrapperBase):
+
+    __bound_function_wrapper__ = BoundFunctionWrapper
+
+    def __init__(self, wrapped, wrapper, enabled=None):
+        # What it is we are wrapping here could be anything. We need to
+        # try and detect specific cases though. In particular, we need
+        # to detect when we are given something that is a method of a
+        # class. Further, we need to know when it is likely an instance
+        # method, as opposed to a class or static method. This can
+        # become problematic though as there isn't strictly a fool proof
+        # method of knowing.
+        #
+        # The situations we could encounter when wrapping a method are:
+        #
+        # 1. The wrapper is being applied as part of a decorator which
+        # is a part of the class definition. In this case what we are
+        # given is the raw unbound function, classmethod or staticmethod
+        # wrapper objects.
+        #
+        # The problem here is that we will not know we are being applied
+        # in the context of the class being set up. This becomes
+        # important later for the case of an instance method, because in
+        # that case we just see it as a raw function and can't
+        # distinguish it from wrapping a normal function outside of
+        # a class context.
+        #
+        # 2. The wrapper is being applied when performing monkey
+        # patching of the class type afterwards and the method to be
+        # wrapped was retrieved direct from the __dict__ of the class
+        # type. This is effectively the same as (1) above.
+        #
+        # 3. The wrapper is being applied when performing monkey
+        # patching of the class type afterwards and the method to be
+        # wrapped was retrieved from the class type. In this case
+        # binding will have been performed where the instance against
+        # which the method is bound will be None at that point.
+        #
+        # This case is a problem because we can no longer tell if the
+        # method was a static method, plus if using Python3, we cannot
+        # tell if it was an instance method as the concept of an
+        # unnbound method no longer exists.
+        #
+        # 4. The wrapper is being applied when performing monkey
+        # patching of an instance of a class. In this case binding will
+        # have been perfomed where the instance was not None.
+        #
+        # This case is a problem because we can no longer tell if the
+        # method was a static method.
+        #
+        # Overall, the best we can do is look at the original type of the
+        # object which was wrapped prior to any binding being done and
+        # see if it is an instance of classmethod or staticmethod. In
+        # the case where other decorators are between us and them, if
+        # they do not propagate the __class__  attribute so that the
+        # isinstance() checks works, then likely this will do the wrong
+        # thing where classmethod and staticmethod are used.
+        #
+        # Since it is likely to be very rare that anyone even puts
+        # decorators around classmethod and staticmethod, likelihood of
+        # that being an issue is very small, so we accept it and suggest
+        # that those other decorators be fixed. It is also only an issue
+        # if a decorator wants to actually do things with the arguments.
+        #
+        # As to not being able to identify static methods properly, we
+        # just hope that that isn't something people are going to want
+        # to wrap, or if they do suggest they do it the correct way by
+        # ensuring that it is decorated in the class definition itself,
+        # or patch it in the __dict__ of the class type.
+        #
+        # So to get the best outcome we can, whenever we aren't sure what
+        # it is, we label it as a 'callable'. If it was already bound and
+        # that is rebound later, we assume that it will be an instance
+        # method and try and cope with the possibility that the 'self'
+        # argument it being passed as an explicit argument and shuffle
+        # the arguments around to extract 'self' for use as the instance.
+
+        binding = None
+
+        if isinstance(wrapped, _FunctionWrapperBase):
+            binding = wrapped._self_binding
+
+        if not binding:
+            if inspect.isbuiltin(wrapped):
+                binding = 'builtin'
+
+            elif inspect.isfunction(wrapped):
+                binding = 'function'
+
+            elif inspect.isclass(wrapped):
+                binding = 'class'
+
+            elif isinstance(wrapped, classmethod):
+                binding = 'classmethod'
+
+            elif isinstance(wrapped, staticmethod):
+                binding = 'staticmethod'
+
+            elif hasattr(wrapped, '__self__'):
+                if inspect.isclass(wrapped.__self__):
+                    binding = 'classmethod'
+                elif inspect.ismethod(wrapped):
+                    binding = 'instancemethod'
+                else:
+                    binding = 'callable'
+
+            else:
+                binding = 'callable'
+
+        super(FunctionWrapper, self).__init__(wrapped, None, wrapper,
+                enabled, binding)