This release is a pre-release and may not be stable for production use.
EvalCache
Decorator-first caching for graphs of expensive Python computations.
EvalCache turns calls to pure functions into deferred expression nodes. The result of every node can be reused from memory or a persistent cache, including intermediate nodes shared by several computations.
Install
python -m pip install evalcache
Basic use
import evalcache
evalcache.configure(
cache_store=evalcache.DirectoryCacheStore(".evalcache"),
)
@evalcache.operation
def preprocess(source: str) -> list[int]:
print("preprocessing", source)
return [1, 2, 3]
@evalcache.operation
def total(values: list[int]) -> int:
return sum(values)
result = total(preprocess("model.step"))
# Decorated calls build a graph and return Deferred values.
assert isinstance(result, evalcache.Deferred)
print(result.compute())
On the first run, EvalCache evaluates and stores both operations. A fresh
process can restore their results from .evalcache when their operation
identity and arguments have not changed.
The default serializer uses pickle. Only open cache directories trusted by the current user.
Operation identity
By default, EvalCache derives an operation id from the function's module and qualified name and derives a version from its Python implementation. For long-lived caches, explicit identity makes invalidation intentional:
@evalcache.operation(
operation_id="my-project.build-mesh",
operation_version="3",
result=Mesh,
)
def build_mesh(source: Source) -> Mesh:
...
Keep operation_id stable while the operation retains the same meaning.
Increment operation_version when old cached results must no longer be used.
A return annotation normally supplies the result contract; result= can
instead accept a runtime type or a ResultSpec with a validator or custom
serializer.
Domain arguments must have deterministic identities. Register an encoder with
HashRegistry or implement __evalcache_key__() -> bytes:
hashes = evalcache.HashRegistry()
hashes.register(Source, lambda source: source.digest.encode("ascii"))
@evalcache.operation(hash_registry=hashes)
def convert(source: Source) -> Mesh:
...
See advanced_decorators.py for operation versions, validation, domain hashing, progress events, and non-cacheable operations.
Policies and explicit evaluators
Module-level @evalcache.operation uses the default evaluator at call time.
configure() replaces its global policies without redecorating functions.
For libraries or isolated jobs, own the evaluator explicitly:
evaluator = evalcache.Evaluator(
cache_store=evalcache.DirectoryCacheStore(".evalcache"),
cache_policy=evalcache.CachePolicy(namespace="geometry"),
)
@evaluator.operation
def triangulate(shape: Shape) -> Mesh:
...
using_evaluator() temporarily changes the default evaluator. Deferred
values owned by different evaluators cannot be mixed in one graph.
CachePolicy controls persistent reads, writes, namespaces, and corrupt
record recovery. In-memory reuse within an evaluator remains enabled even when
CachePolicy.disabled() disables persistent caching.
Composing Deferred values
Arguments may contain other Deferred values inside lists, tuples, sets, frozensets, and mappings. Operators with unambiguous lazy semantics create new expression nodes without computing their operands:
@evalcache.operation
def load_values() -> tuple[int, ...]:
return (6, -4, 3)
values = load_values()
result = 10 + values[0] * 2 - abs(values[1])
assert result.compute() == 18
Unary +, -, abs, and ~ are supported, together with arithmetic,
matrix, bitwise, shift, reflected binary operators, and indexing. Comparisons,
truth testing, and implicit iteration require an explicit .compute().
See expression_tree.py and operators.py.
File artifacts
A file-producing operation can return immutable contents while leaving the destination path outside expression identity:
@evalcache.operation(
operation_id="my-project.render-report",
operation_version="1",
result=evalcache.file_artifact_result(),
)
def render_report(value: int) -> evalcache.FileArtifact:
return evalcache.FileArtifact(
name="report.txt",
data=("result={}\n".format(value)).encode("utf-8"),
media_type="text/plain",
)
report = render_report(42)
report.materialize("first.txt")
report.materialize("second.txt")
FileArtifact.from_path() snapshots a backend that can only produce a file.
materialize() atomically replaces the selected destination. The current
serializer stores artifact bytes inside the cache record.
See file_artifact.py.
Low-level API
Decorators are the normal entry point. Applications that need explicit domain
handles may construct Expression[T] values and submit or evaluate them with
Evaluator. Storage and serialization are replaceable through the
CacheStore and Serializer protocols; MemoryCacheStore,
DirectoryCacheStore, and MappingCacheStore are included.
Original Lazy API
The original implementation remains available under evalcache.legacy:
from evalcache.legacy import DirCache, Lazy
lazy = Lazy(cache=DirCache(".evalcache"))
@lazy
def calculate(value):
return value * 2
assert calculate(21).unlazy() == 42
The historical top-level imports remain as compatibility aliases, but new code
should use @evalcache.operation. See Legacy API and migration
and the legacy examples.
Further reading
License
EvalCache is distributed under the MIT License.
Contact: mirmik (mirmikns@yandex.ru)
Metadata
Release files for evalcache 2.0.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| evalcache-2.0.0a1.tar.gz | 44.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| evalcache-2.0.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.6 kB
Release files / evalcache-2.0.0a1.tar.gz
| Download URL | evalcache-2.0.0a1.tar.gz |
|---|---|
| Size | 44.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4a1dfb28d3e652454a0feb9e2846472890f84895466c8503a8cef11aa93d1847
|
|
BLAKE2b-256 checksum How to use checksums |
fd872e2564fdc56202da738be24424531db69bfa37c3956d17d9b6554edca96e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|
Release files / evalcache-2.0.0a1-py3-none-any.whl
| Download URL | evalcache-2.0.0a1-py3-none-any.whl |
|---|---|
| Size | 35.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
45eb44cb1eec5e750be8aefb6b1bda9ff0a8a3c73d5441ba420160b58c697be7
|
|
BLAKE2b-256 checksum How to use checksums |
8cc5d9d61e1cefc87f68bf8e9767146dfa34fff8a804f40fc66ead031a129c77
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|