Skip to main content

lrucheck logo

lrucheck

PyPI Python versions Downloads License: MIT Ruff pre-commit

A small static checker that finds memory leaks from functools.lru_cache and functools.cache in Python code.

Why

Python's lru_cache is easy to use, but it has two common traps:

  1. @lru_cache on a method. The cache holds the self argument. Your instance never gets garbage-collected. The longer the program runs, the more memory it uses.
  2. @lru_cache(maxsize=None). The cache has no size limit. It grows forever as new arguments come in. (@cache does the same thing but is fine when you want it on purpose, for example on a method of an enum.Enum subclass.)

lrucheck reads your code (without running it) and prints a warning when it finds these patterns.

Install

uv add lrucheck

Use

Scan a file:

lrucheck path/to/file.py

Scan a folder (recursive):

lrucheck src/

Example

Given this file service.py:

from functools import lru_cache


class UserService:
    @lru_cache(maxsize=None)
    def find_user(self, user_id):
        return load_user(user_id)

    @lru_cache(maxsize=128)
    def get_settings(self, user_id):
        return load_settings(user_id)

Run lrucheck:

$ lrucheck service.py
service.py:5:6: LRU001 `@lru_cache` on a method keeps `self` in the cache and leaks the instance
service.py:5:6: LRU002 `@lru_cache(maxsize=None)` has no size limit and can grow forever. Use `@cache` directly if this is on purpose.
service.py:9:6: LRU001 `@lru_cache` on a method keeps `self` in the cache and leaks the instance
$ echo $?
1

The output format is the same as flake8 and ruff, so editors and CI tools can read it.

Rules

Code What it finds
LRU001 @lru_cache or @cache on a method. The cache keeps self, so the instance is never freed.
LRU002 @lru_cache(maxsize=None). The cache has no size limit and can grow forever. @cache is not flagged because it is sometimes the right choice.
LRU003 @lru_cache inside a function or closure. A new cache is made on every outer call, so cache hits are rare. (warning)
LRU004 @lru_cache placed above @staticmethod. Wrong decorator order. The reverse breaks on Python 3.9 and is non canonical on later versions. (warning)

Bad

from functools import lru_cache

class Service:
    @lru_cache(maxsize=None)   # LRU001 + LRU002
    def fetch(self, key):
        return load(key)

Good

from functools import lru_cache

@lru_cache(maxsize=128)
def fetch(key):
    return load(key)

If you must keep the cache near a class, use a @staticmethod or a top-level function:

class Service:
    @staticmethod
    @lru_cache(maxsize=128)
    def fetch(key):
        return load(key)

Severity

Each rule has a level. Errors (LRU001, LRU002) point to real memory leaks and fail the build. Warnings (LRU003, LRU004) point to less serious problems and do not change the exit code on their own. Warnings still print to the output and start with the warning: prefix.

service.py:5:6: LRU001 ...                  (error)
service.py:5:6: warning: LRU003 ...         (warning)

Exit codes

Code Meaning
0 No errors found. Warnings may still be printed.
1 One or more rule errors found.
2 A path was missing, a file could not be read, or a file had a syntax error.

This makes lrucheck easy to use in CI: a non-zero exit fails the build.

Pre-commit (planned)

A pre-commit hook is on the roadmap (see TODO.md). For now, you can run lrucheck from a script or a Makefile.

Roadmap

  • Read config from pyproject.toml [tool.lrucheck]
  • # noqa: LRU001 to skip a single line
  • --select and --ignore to turn rules on or off
  • JSON output for editors

See TODO.md for the full list.

Development

This project uses uv.

uv sync
uv run pytest
uv run lrucheck tests/examples/

Pre-commit

The project uses pre-commit to run checks before each commit:

  • ruff for linting and formatting
  • codespell for spelling
  • ty for type checking
  • standard hooks for trailing whitespace, end of file, large files

To set it up once:

uv run pre-commit install

After this, the hooks run on every git commit. You can also run them on all files at any time:

uv run pre-commit run --all-files

License

MIT.

Metadata

Release files for lrucheck 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lrucheck 0.2.0
File Size Uploaded
lrucheck-0.2.0.tar.gz 8.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lrucheck 0.2.0
File Interpreter ABI Platform
lrucheck-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 16.9 kB

Release files / lrucheck-0.2.0.tar.gz

Download URL lrucheck-0.2.0.tar.gz
Size 8.8 kB
Tags Source
SHA-256 checksum
How to use checksums
85d0c51824deb591937045592d35b89cffbae8fdb56c9961c236f2ff186736dd
BLAKE2b-256 checksum
How to use checksums
a93229edec939572e0e35be54ad4ab1915911971626065f4070d27f0c529bfbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / lrucheck-0.2.0-py3-none-any.whl

Download URL lrucheck-0.2.0-py3-none-any.whl
Size 8.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
12ed7dce2e2af97a61ddd8e6beb1ee0443cb83330bfbae4be86489a8b4feced9
BLAKE2b-256 checksum
How to use checksums
4905fb7addf8c66798d8d0754302625b52871e90764cae47d7e805e4cb14119e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page