# cachetools: memoizing collections for Python, where the standard library stops

> A small MIT-licensed library of bounded, mutable mapping types and the decorators that use them. It exists to fill the gap between functools.lru_cache and a cache with an eviction policy you choose.

**tkem/cachetools** — Extensible memoizing collections and decorators

- Repository: https://github.com/tkem/cachetools
- Stars: 2,780 · Forks: 211
- Language: Python
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/tkem-cachetools

## Filling the gap between lru_cache and a real cache

The module's own summary names its relationship to the standard library: it provides memoizing collections and decorators, including variants of the Python Standard Library's `@lru_cache` function decorator. That framing matters. `functools.lru_cache` is a decorator with a fixed-size cache and one eviction strategy, and once you need anything beyond that you either write a dict wrapper yourself or pull in a dependency.

cachetools takes the second approach and stops there. It supplies the cache objects themselves, as mutable mappings with a fixed maximum size, and the decorators that consume them. When such a cache is full, meaning another item would exceed the maximum, it must choose which item to discard based on a cache algorithm. The module's job is to give you a set of those algorithms as ready-made classes rather than to build one.

The practical upshot is that the choice of eviction policy becomes a constructor argument instead of a design project. The classes cover the familiar range, from least-recently-used to time-based expiry, and because they are plain mutable mappings you can also use them directly as a bounded dictionary in code that never calls a function.

## Installing from PyPI and importing the cache classes

Installation is the usual single line from PyPI:

```bash
pip install cachetools
```

The project's own metadata puts the floor at Python 3.10, with classifiers listing 3.10 through 3.15, and the development status classifier reads `5 - Production/Stable`. The build uses setuptools with setuptools-scm, so the version is derived from the git tag rather than hardcoded in `pyproject.toml`, where it appears as a dynamic attribute read from `cachetools.__version__`.

The README's example imports three names and uses them in three different ways, which is the fastest way to understand the design:

```python
from cachetools import cached, LRUCache, TTLCache

# speed up calculating Fibonacci numbers with dynamic programming
@cached(cache={})
def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)
```

The bare `@cached(cache={})` case uses a plain dictionary as the cache, which the README shows to make the point that any mapping will do. The decorator does not care whether the mapping evicts anything.

```python
# cache least recently used Python Enhancement Proposals
@cached(cache=LRUCache(maxsize=32))
def get_pep(num):
    url = 'http://www.python.org/dev/peps/pep-%04d/' % num
```

Here the mapping is bounded at 32 entries and evicts by recency. The third example uses `TTLCache(maxsize=1024, ttl=600)`, described in the README as caching weather data for no longer than ten minutes, where the eviction trigger is age rather than volume.

## Extensibility is the design, not a feature bolted on

The description on GitHub is two words: extensible memoizing collections and decorators. Extensible is the operative one. The cache classes are meant to be subclassed and combined, and the decorators are thin enough that writing your own policy does not mean abandoning the library.

The repository supports that claim with a companion ecosystem listed in the README's Related Projects section. `asyncache` and `cachetools-async` exist specifically to use cachetools with asyncio, which tells you the library's core is synchronous and the async support is a layer on top. `CacheToolsUtils` provides stackable cache classes for sharing, encryption, statistics and more, built on top of cachetools, redis and memcached. `shelved-cache` is a persistent cache implementation, and `cacheing` is an older pure-Python caching library in the same space.

Two of those entries are honest signals about the library's boundaries. Persistence and distributed caching are not part of cachetools; a separate project provides each. And the async story is not built in, so a codebase built on asyncio needs an extra dependency before these decorators are usable. For a synchronous service, neither caveat applies.

## Where the project keeps its documentation and its history

The README is written in reStructuredText and is deliberately short. Documentation lives at cachetools.readthedocs.io, with a documentation build status badge pointing at a Read the Docs project configured by `.readthedocs.yaml` in the repository. The changelog is `CHANGELOG.rst`, linked directly from the README, which is a better sign than a changelog hosted on a separate site.

The repository layout is small and conventional for a Python package of this kind: `src/` holding the package, `tests/` for the suite, `docs/`, `tox.ini` for running tests across environments, and `MANIFEST.in` for the source distribution. Badges cover the CI workflow status, test coverage through Codecov, the license, and the latest PyPI version.

Licensing is unambiguous: MIT, with a LICENSE file at the root and a copyright line reading `Copyright (c) 2014-2026 Thomas Kemmer`. The same author is listed as both author and maintainer in the project metadata, with a contact address, which for a library this widely depended on is either a strength or a single point of failure depending on your priorities.

The repository has no GitHub releases attached, so the changelog file is the version history. The last push was on 2026-09-23 and the repository is not archived.

## What cachetools does not solve about caching

A bounded mapping with a chosen eviction policy solves memory growth. It does not solve staleness, and the README is direct about the shape of a cache: it is a mutable mapping of a fixed maximum size, and full means items must go. Nothing in the library warns a caller that the data they just read was fetched before the upstream changed.

The three examples in the README make the risk visible if you read them closely. The Fibonacci memoization is safe because pure functions return the same answer forever. The PEP example is nearly safe, since the Python Enhancement Proposals at those URLs are effectively immutable once accepted. The weather example is a live upstream behind a ten-minute TTL, and it is the one that will eventually serve you a stale forecast. The library cannot distinguish these cases; the author of the cached function has to decide what the acceptable staleness is.

There is also a narrower trap. Because any mapping can be passed as the cache, including a bare `{}`, it is easy to end up with an unbounded cache by accident and believe you configured a bounded one. Checking `isinstance` or simply reading back the constructor you wrote is the whole defence. For anything shared between processes or machines, remember that persistence and distribution are explicitly other projects' jobs.

## Conclusion

cachetools is worth reaching for the moment your memoization needs a policy the standard library does not offer: a TTL, a custom eviction order, a shared cache between methods of one object. It is small, dependency-free, typed, and its author is still pushing to it, with the last commit on 2026-09-23 and a changelog maintained in the repository. What it will not do is make caching correct for you; the README's warning about stale data applies to every cache you build on it. Start with `TTLCache` when freshness is the requirement and `LRUCache` when memory pressure is, and read the readthedocs documentation before you subclass anything, because the mapping and decorator contracts are stricter than they first appear.

## FAQ

### What is cachetools used for in Python?

It provides memoizing collections and decorators, including variants of the standard library's lru_cache decorator. You pass a bounded mutable mapping such as LRUCache or TTLCache to the cached decorator, and the mapping decides which entries to discard when it is full.

### How do you install cachetools?

It is published on PyPI and installed with pip install cachetools. The project metadata requires Python 3.10 or newer and classifies the package as Production/Stable, with classifiers covering Python 3.10 through 3.15.

### What is the difference between LRUCache and TTLCache in cachetools?

LRUCache evicts by recency, discarding the least recently used entry once maxsize is reached. TTLCache evicts by age, discarding entries older than the ttl value in seconds once the cache is full, which the README demonstrates with a ten-minute limit for weather data.

### Does cachetools work with asyncio?

The library itself is synchronous. The README's Related Projects section lists asyncache and cachetools-async as separate packages that provide helpers for using cachetools with asyncio, so async code needs one of those as an extra dependency.

### What license is cachetools released under?

The MIT License, with a LICENSE file in the repository root and a copyright line naming Thomas Kemmer for the years 2014 to 2026.

## Sources

- [Issues](https://github.com/tkem/cachetools/issues)
- [License: MIT](https://github.com/tkem/cachetools/blob/master/LICENSE)
- [README](https://github.com/tkem/cachetools/blob/master/README.md)
- [tkem/cachetools on GitHub](https://github.com/tkem/cachetools)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tkem-cachetools
