Introduction to Wrapt
The Wrapt library is an innovative Python library designed to provide highly flexible decorators and function-wrapping utilities. It is particularly useful for building decorators with complete control over the underlying wrapped functions, all while maintaining improved introspection support. Wrapt helps developers create reusable, maintainable, and powerful decorators, making it a go-to library for advanced Python programming tasks.
Highlights of Wrapt Library
Wrapt stands out for its unique ability to properly wrap functions and methods. Unlike many decorators, it retains correct metadata and handles corner cases in callable objects effectively. Wrapt facilitates:
- Advanced function wrapping
- Thread-safe decorator creation
- Consistent API ensuring introspection integrity
Key Features and APIs
Below, we explain several important APIs in Wrapt with examples:
1. ObjectProxy
: Transparent Proxying
Wrapt’s ObjectProxy
enables the creation of a transparent proxy to wrap an object, offering advanced introspection capabilities.
from wrapt import ObjectProxy class MyProxy(ObjectProxy): def __init__(self, wrapped): super().__init__(wrapped) def __str__(self): return f"MyProxy({super().__str__()})" obj = MyProxy(42) print(obj) # Output: MyProxy(42)
2. FunctionWrapper
: Flexible Function Wrapping
The FunctionWrapper
API enables developers to wrap functions while allowing complete control over argument passing and invocation. Here’s an example:
from wrapt import FunctionWrapper def my_wrapper(wrapped, instance, args, kwargs): print("Before function call") result = wrapped(*args, **kwargs) print("After function call") return result def example_function(x, y): return x + y wrapped_function = FunctionWrapper(example_function, my_wrapper) print(wrapped_function(3, 7)) # Output: 'Before function call', 'After function call', followed by 10
3. decorate
: Simplified Decorator Creation
The decorate
API simplifies the process of creating decorators by applying your custom wrapper to specific functions.
from wrapt import decorate def my_wrapper(wrapped, instance, args, kwargs): print(f"Calling {wrapped.__name__} with {args} and {kwargs}") return wrapped(*args, **kwargs) @decorate(my_wrapper) def say_hello(name): return f"Hello, {name}" print(say_hello("Alice")) # Output: "Calling say_hello with ('Alice',) and {}", "Hello, Alice"
4. bound_function_wrapper
: Method Wrapping Made Easy
The bound_function_wrapper
API eases the process of wrapping methods in classes.
from wrapt import bound_function_wrapper class MyClass: def greet(self, name): return f"Hello, {name}" def my_wrapper(wrapped, instance, args, kwargs): print(f"Instance: {instance}, Calling: {wrapped.__name__}") return wrapped(*args, **kwargs) obj = MyClass() obj.greet = bound_function_wrapper(obj.greet, obj, my_wrapper) print(obj.greet("Alice")) # Logs wrapper details, followed by "Hello, Alice"
Building a Real-World Application
Let’s build a small web logging library using Wrapt to decorate HTTP request handling functions:
from wrapt import decorator # Custom logging decorator @decorator def request_logger(wrapped, instance, args, kwargs): method = kwargs.get('method', 'GET') endpoint = args[0] if args else '/' print(f"[LOG] Handling {method} request at {endpoint}") return wrapped(*args, **kwargs) @request_logger def handle_request(endpoint, method="GET"): return f"Response for {method} request at {endpoint}" # Simulate request handling print(handle_request("/home", method="POST")) print(handle_request("/about"))
Output:
[LOG] Handling POST request at /home Response for POST request at /home [LOG] Handling GET request at /about Response for GET request at /about
Conclusion
Wrapt is an indispensable library for developers seeking robust and flexible solutions for decorators and function wrapping. Its support for introspection, thread safety, and an elegant API makes it a leading choice in Python projects. Try leveraging Wrapt in your project to simplify complex decorator logic and improve application design.