python-lazy-object-proxy
A fast, dependency-free lazy object proxy for Python that defers construction until first use.
Repository Health
Technical Analysis
lazy-object-proxy is a small, dependency-free Python library that defers the creation of an object until it’s actually accessed. You wrap a zero-argument factory function in a Proxy, and the target object is only constructed the first time an attribute, method call, or operator touches the proxy — after which the resolved value is cached and reused for the life of the proxy instance.
The library ships three interchangeable backends behind one public API: a compiled C extension for maximum speed, a slots-based pure-Python implementation, and a plain dict-based pure-Python fallback for interpreters (like PyPy and GraalPy) where the C extension can’t be built. It’s a direct descendant of GrahamDumpleton’s wrapt.ObjectProxy, forked specifically to add true lazy evaluation, and its exhaustive dunder-method coverage (including async iteration, awaiting, and context-manager protocols) makes proxied objects behave indistinguishably from the real thing in almost every situation.
What You Get
- A
Proxy(factory)class that transparently forwards nearly every Python operation to a lazily-constructed target object. - Three interchangeable backends — a compiled C extension, a slots-based pure-Python implementation, and a plain-Python fallback — selected automatically at import time.
- Full async support, including
__await__,__aiter__/__anext__, and__aenter__/__aexit__, so proxies work transparently with coroutines and async context managers. - Zero runtime dependencies and wheels built for CPython 3.9-3.14 plus PyPy and GraalPy.
Common Use Cases
- Deferring expensive imports or computations in a Django/Flask settings module until the value is actually read.
- Building placeholder objects for dependency injection where the real service isn’t available until later in the request lifecycle.
- Wrapping thread-local or request-scoped objects that shouldn’t be resolved until they’re used inside the current context.
- Implementing lazy configuration or connection objects that shouldn’t touch the network or filesystem at import time.
Under The Hood
Architecture
The core abstraction is the Proxy(factory) object exposed from src/lazy_object_proxy/__init__.py, which stores a callable and lazily resolves __wrapped__ on first attribute access, caching the result thereafter; virtually every dunder method is forwarded to __wrapped__ through wrapper methods generated in simple.py’s make_proxy_method. The package implements this abstraction three separate times behind one public name: a compiled C extension (cext.c, ~1,450 lines), a slots-based pure-Python implementation (slots.py, using a _ProxyMetaType metaclass to inject _ProxyMethods properties for __module__/__doc__/__dict__/__weakref__), and a plain dict-based implementation (simple.py). __init__.py tries the C extension first and falls back to simple.py if the import fails, while shared async/utility helpers (await_, cached_property, identity, and the async dunder implementations) live in utils.py and small compatibility shims in compat.py. This tiered, swappable-backend design means removing or failing to build the C extension degrades performance but never breaks functionality.
Tech Stack
Pure Python (3.9+) with an optional CPython C extension, built via setuptools + setuptools_scm (pyproject.toml, build-backend setuptools.build_meta) and zero runtime dependencies (dependencies = []). Dev/test tooling includes pytest and pytest-benchmark, with Django, objproxies, and wrapt pulled in only for benchmark comparisons. Linting and formatting run through ruff/ruff-format and taplo via pre-commit, tox drives multi-environment test matrices, and GitHub Actions builds wheels across CPython 3.9-3.14 plus PyPy/GraalPy (where the C extension is disabled). Docs are built with Sphinx and hosted on Read the Docs.
Code Quality
Two large, dunder-by-dunder test suites — tests/test_lazy_object_proxy.py (1,894 lines) and tests/test_async_py3.py (1,752 lines) — exercise the proxy’s full special-method surface and async behavior via pytest, parametrized across all three backends, plus dedicated pytest-benchmark performance tests. Coverage is tracked through Coveralls and Codecov and enforced in CI on every push, and pre-commit hooks run ruff, taplo, trailing-whitespace, end-of-file, and debug-statement checks before merge. There is no static type-checking configuration (no mypy/pyright setup), so type safety relies on the extensive test suite rather than type annotations.
API Design
The public surface is deliberately tiny — a single Proxy class imported as from lazy_object_proxy import Proxy and constructed from a zero-argument factory, with no subclassing or configuration required to get started, exactly as shown in the one-line README example. It positions itself precisely against wrapt.ObjectProxy (which it forks from and credits): where wrapt.ObjectProxy forwards to an already-resolved target, this library evaluates the factory once, lazily, on first use — a narrow but clearly documented behavioral distinction. Backend selection (C extension vs. slots vs. simple) is fully transparent to callers, so the fast path never requires different calling code. The scope is intentionally narrow, so there’s little surface area to document beyond the README and changelog, and there are no dedicated async-usage docs beyond what the test suite demonstrates.
Used by 2 apps in this directory
Apache Airflow
Data Engineering
Define, schedule, and monitor complex data workflows as Python code — with a powerful UI, 80+ provider integrations, and battle-tested scalability across thousands of production deployments.
Grist
Databases · No Code Platforms
A modern relational spreadsheet that combines Python-powered formulas, drag-and-drop dashboards, and granular access controls in a self-hostable, SQLite-backed data platform.