Migrations

Upgrade guides for breaking changes to the cachetta PyPI package. Each entry is required to include all five sections below. Write “None.” if a section truly has no content, so reviewers know it was considered.

v0.7 → v0.8

Summary

The skip_self option has been removed. It existed so that decorating an instance/class method wouldn’t fold the receiver (self/cls) into the cache key, but it worked by blindly stripping the first positional argument whenever set — with no check that the call was actually a method, so setting it on a plain function silently discarded a real argument. Receiver exclusion is now automatic: the decorator detects method binding via the descriptor protocol and strips the receiver from key/path resolution while still passing it to the wrapped function. Plain functions keep all of their positional arguments. Because detection is automatic and correct, the flag is no longer needed and passing it raises TypeError.

Required changes

| Before | After | |——–|——-| | @Cachetta(path=fn, skip_self=True)
def method(self, x): ... | @Cachetta(path=fn)
def method(self, x): ...
receiver excluded automatically | | cache = Cachetta(path=..., skip_self=True) | cache = Cachetta(path=...) | | cache.copy(skip_self=True) | cache.copy() |

Deprecations removed

  • The skip_self field on Cachetta (and the skip_self= keyword to its constructor, copy, and per-decoration overrides). It was never deprecated with a warning; it is removed outright.

Behavior changes without code changes

  • Decorating an instance/class method now excludes the receiver from the cache key by default. Code that previously relied on the old default (skip_self=False) to key on the receiver — e.g. a callable path or hashed=True whose key intentionally varied per instance — will now share cache entries across instances for equal arguments. This was almost never intentional (object identity is not stable across runs), but if you need per-instance partitioning, key on an explicit instance attribute via a callable path.

Verification

  • After upgrading, confirm the receiver no longer reaches a callable path:
    from cachetta import Cachetta
    
    seen = []
    cache = Cachetta(path=lambda x: seen.append(x) or f"/tmp/{x}.dat")
    
    class Svc:
        @cache
        def get(self, x):
            return x
    
    Svc().get("a")
    assert seen == ["a"]  # the path callable saw "a", not (self, "a")
    

    Pre-upgrade (without skip_self=True) this raised TypeError because the lambda received self as an extra positional argument.

v0.6 → v0.7

Summary

The implicit “sibling-hash” behavior has been removed. When path was a str or Path, cachetta used to silently rewrite the filename into a {stem}-{hash}{ext} sibling whenever the wrapped function received arguments. That default collided across functions that happened to share a stem in the same folder, left a dead stem prefix on every file name, and gave callers no clean way to organize multiple cache kinds under one root. path=str|Path now means exactly that: cachetta writes to the literal path you gave it, regardless of arguments.

Required changes

| Before | After | |——–|——-| | @Cachetta(path="cache/llm.pkl")
def call(prompt): ...
wrote to cache/llm-<hash>.pkl | @Cachetta(path=lambda prompt: f"cache/llm/{prompt}.pkl")
def call(prompt): ...
explicit per-arg path callable | | cache = Cachetta(path="cache.json")
cache.invalidate("a")
removed cache-<hash>.json | cache = Cachetta(path=lambda x: f"cache/{x}.json")
cache.invalidate("a")
removes the path you actually wrote |

If a single literal file is what you actually want, no change is needed — that’s the new default.

Deprecations removed

  • The implicit {stem}-{hash}{ext} sibling rewrite in Cachetta._get_path when path is a str or Path.

Behavior changes without code changes

  • Cachetta(path="cache.json") decorating a function with arguments now writes every call to cache.json (last write wins) instead of fanning out into per-args sibling files. Existing on-disk cache-<hash>.json files written by older versions are no longer read; delete or migrate them by hand if you need their contents.

Verification

  • After upgrading, run:
    from cachetta import Cachetta
    cache = Cachetta(path="/tmp/cachetta_check.pkl")
    assert str(cache._get_path("anything")) == "/tmp/cachetta_check.pkl"
    

    The assertion should succeed; pre-upgrade it produced a path of the form /tmp/cachetta_check-<hash>.pkl.