wrapt

A Python module for building correct decorators, transparent object proxies, and safe monkey patching.

Library
PyPI
v2.5.1
2,312 stars
BSD-2-Clause

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
88 /100 Excellent
Development Activity 96
Maintenance 84
Community 72
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
79 /100 Good
Architecture 85
Code Quality 88
Innovation 80
Learning Curve 62

wrapt provides a transparent object proxy for Python that serves as the foundation for writing decorators and wrapper functions that behave correctly in far more scenarios than functools.wraps() alone can guarantee. It goes beyond simply copying __name__/__doc__ attributes, preserving introspectability, call signatures, and type-checking behavior so that decorated functions, methods, classmethods, staticmethods, and classes all continue to behave like their undecorated originals.

The module ships a C extension for performance-critical proxy operations, with an automatic pure-Python fallback when no compiler is available, and includes dedicated utilities for monkey patching modules and classes safely at runtime. Because of its correctness-first design, wrapt is widely used as a dependency inside instrumentation, APM, and observability libraries (such as tracing agents) that need to wrap arbitrary third-party functions without breaking their callers’ assumptions.

What You Get

  • A @wrapt.decorator pattern for building ‘universal’ decorators that correctly detect whether they wrap a function, instance method, classmethod, or class
  • A transparent ObjectProxy base class for building advanced wrapper objects that behave identically to the wrapped object
  • Monkey-patching utilities (wrapt.wrap_function_wrapper, wrapt.patch_function_wrapper) for safely instrumenting third-party code at runtime
  • A C extension (_wrappers.c) for performance-critical proxy operations, with an automatic pure-Python fallback when no compiler is present
  • Thread-safe decorator implementations via a dedicated synchronization.py module
  • Full preservation of function signatures, annotations, and other introspection metadata through the decoration process

Common Use Cases

  • Writing decorators for a library’s public API that must preserve the wrapped function’s signature for tools like IDEs, inspect, and type checkers
  • Building an APM or tracing agent that monkey-patches third-party functions to add instrumentation without altering their observable behavior
  • Implementing lazy-loading, deprecation-warning, or retry decorators that need to work uniformly across functions, methods, and classes
  • Constructing object proxies (e.g. lazy objects, access-controlled wrappers) that must be indistinguishable from the object they wrap

Under The Hood

Architecture: The core is an ObjectProxy (src/wrapt/wrappers.py, src/wrapt/proxies.py) implemented primarily in C (src/wrapt/_wrappers.c) for speed, with a parallel pure-Python implementation used automatically when the C extension can’t be compiled — both expose the identical Python-level API so callers never need to know which backend is active. src/wrapt/decorators.py builds the @wrapt.decorator mechanism on top of this proxy, passing (wrapped, instance, args, kwargs) to the wrapper function so the decorator can introspect exactly what kind of callable it’s wrapping (plain function, bound method, classmethod, or class) via the instance parameter — this is the mechanism behind wrapt’s ‘universal decorator’ claim. src/wrapt/importer.py and patches.py implement the monkey-patching layer, hooking module import machinery so patches can be applied to functions/classes at the moment they’re first imported rather than requiring the patched module to already be loaded. src/wrapt/signature.py and arguments.py handle signature introspection and argument binding so decorated callables continue to report accurate inspect.signature() results. Tech Stack: Python 3.9+, CPython and PyPy supported, built with setuptools (PEP 639 license metadata) and uv for dependency locking (uv.lock); the C extension is optional and compiled via setup.py’s standard build_ext machinery. Code Quality: The tests/ directory contains 124 test files, an unusually large and thorough suite for a library of this size, reflecting the project’s explicit goal of focusing very much on correctness — tests cover decorator behavior across every callable type, proxy transparency, monkey-patching edge cases, and both the C and pure-Python backends to ensure behavioral parity. The repository also documents its own AI-assistant contribution guidelines (AGENTS.md, CLAUDE.md), suggesting active, disciplined maintenance practices. API Design: The (wrapped, instance, args, kwargs) decorator signature is the project’s signature ergonomic choice — it replaces the usual pattern of writing several near-duplicate decorator variants (one for functions, one for methods, etc.) with a single function that inspects instance to determine context, which the README’s ‘universal decorator’ example demonstrates directly; this does add one non-obvious concept (the meaning of instance) that newcomers must learn relative to a plain functools.wraps()-based decorator.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers