CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wrapt

Module for decorators, wrappers and monkey patching.

Overview
Eval results
Files

proxy-objects.mddocs/

Proxy Objects

Transparent proxy objects that wrap other objects and delegate all operations to the wrapped object while allowing interception and modification of behavior. These proxies preserve the interface of the wrapped object while providing hooks for customization.

Capabilities

ObjectProxy

A transparent proxy object that wraps another object and delegates all operations to the wrapped object. The proxy preserves all attributes, methods, and special behaviors of the wrapped object.

class ObjectProxy:
    def __init__(self, wrapped):
        """
        Create a transparent proxy for the wrapped object.
        
        Args:
            wrapped: The object to wrap
        """

    @property
    def __wrapped__(self):
        """Reference to the wrapped object."""

    def __self_setattr__(self, name, value):
        """
        Set attribute on the proxy itself rather than the wrapped object.
        
        Args:
            name (str): Attribute name
            value: Attribute value
        """

    # Properties that delegate to wrapped object
    @property 
    def __module__(self): ...
    
    @property
    def __doc__(self): ...
    
    @property
    def __dict__(self): ...
    
    @property
    def __name__(self): ...
    
    @property
    def __class__(self): ...

Usage Example:

import wrapt

class LoggingProxy(wrapt.ObjectProxy):
    def __init__(self, wrapped):
        super().__init__(wrapped)
        self._self_call_count = 0
    
    def __getattribute__(self, name):
        if name.startswith('_self_'):
            return object.__getattribute__(self, name)
        
        self._self_call_count += 1
        print(f"Accessing attribute: {name} (call #{self._self_call_count})")
        return super().__getattribute__(name)

# Usage
my_list = [1, 2, 3]
proxy_list = LoggingProxy(my_list)
proxy_list.append(4)  # Logs: "Accessing attribute: append (call #1)"
print(len(proxy_list))  # Logs: "Accessing attribute: __len__ (call #2)"

CallableObjectProxy

Extends ObjectProxy to support callable objects by implementing the call protocol. Use this when wrapping functions, methods, or other callable objects.

class CallableObjectProxy(ObjectProxy):
    def __init__(self, wrapped):
        """
        Create a transparent proxy for a callable object.
        
        Args:
            wrapped: The callable object to wrap (must be callable)
        """

    def __call__(self, *args, **kwargs):
        """
        Make the proxy callable by delegating to wrapped object.
        
        Args:
            *args: Positional arguments
            **kwargs: Keyword arguments
            
        Returns:
            Result of calling the wrapped object
        """

Usage Example:

import wrapt

class TimingProxy(wrapt.CallableObjectProxy):
    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = super().__call__(*args, **kwargs)
        end = time.time()
        print(f"Function took {end - start:.4f} seconds")
        return result

def slow_function(n):
    import time
    time.sleep(n)
    return f"Slept for {n} seconds"

# Wrap the function
timed_function = TimingProxy(slow_function)
result = timed_function(1)  # Prints timing information

PartialCallableObjectProxy

Similar to functools.partial but implemented as a proxy object. Pre-fills some arguments and keyword arguments for a callable, creating a new callable with fewer parameters.

class PartialCallableObjectProxy(ObjectProxy):
    def __init__(self, wrapped, *args, **kwargs):
        """
        Create a partial application of a callable.
        
        Args:
            wrapped: The callable to wrap (must be callable)
            *args: Positional arguments to pre-fill
            **kwargs: Keyword arguments to pre-fill
        """

    @property
    def _self_args(self):
        """Tuple of stored positional arguments."""

    @property  
    def _self_kwargs(self):
        """Dictionary of stored keyword arguments."""

    def __call__(self, *args, **kwargs):
        """
        Call the wrapped function with pre-filled and new arguments.
        
        Args:
            *args: Additional positional arguments
            **kwargs: Additional keyword arguments
            
        Returns:
            Result of calling wrapped function with combined arguments
        """

Usage Example:

import wrapt

def greet(greeting, name, punctuation="!"):
    return f"{greeting}, {name}{punctuation}"

# Create partial applications
say_hello = wrapt.PartialCallableObjectProxy(greet, "Hello")
say_goodbye = wrapt.PartialCallableObjectProxy(greet, "Goodbye", punctuation=".")

# Use the partial functions
print(say_hello("Alice"))        # "Hello, Alice!"
print(say_goodbye("Bob"))        # "Goodbye, Bob."
print(say_hello("Charlie", "?")) # "Hello, Charlie?"

Advanced Usage

Custom Proxy Classes

You can subclass ObjectProxy to create specialized proxy behaviors:

import wrapt

class ValidationProxy(wrapt.ObjectProxy):
    def __init__(self, wrapped, validator=None):
        super().__init__(wrapped)
        self._self_validator = validator or (lambda x: True)
    
    def __setattr__(self, name, value):
        if not name.startswith('_self_') and not self._self_validator(value):
            raise ValueError(f"Invalid value for {name}: {value}")
        super().__setattr__(name, value)

# Usage with validation
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

def age_validator(value):
    return isinstance(value, int) and 0 <= value <= 150

person = Person("Alice", 30)
validated_person = ValidationProxy(person, age_validator)

validated_person.age = 25  # OK
# validated_person.age = -5  # Raises ValueError

Proxy Introspection

All proxy objects maintain access to the wrapped object and preserve introspection capabilities:

import wrapt

def my_function():
    """A test function."""
    return "Hello"

proxy = wrapt.CallableObjectProxy(my_function)

# Access wrapped object
assert proxy.__wrapped__ is my_function

# Introspection works
assert proxy.__name__ == "my_function"
assert proxy.__doc__ == "A test function."
assert callable(proxy)

# isinstance and hasattr work correctly
assert isinstance(proxy, type(my_function))
assert hasattr(proxy, '__call__')

Error Handling

Proxy objects raise NotImplementedError for operations that cannot be safely proxied:

  • __copy__() and __deepcopy__(): Copying behavior is complex and object-specific
  • __reduce__() and __reduce_ex__(): Pickling support requires custom implementation

For these operations, either implement them in your proxy subclass or handle them explicitly in your code.

Install with Tessl CLI

npx tessl i tessl/pypi-wrapt

docs

decorators.md

function-wrappers.md

index.md

patching.md

proxy-objects.md

utilities.md

tile.json