Skip to content

refactor: replace cachetools with stdlib OrderedDict-based LRUCache#36

Open
isaacbmiller wants to merge 1 commit intomainfrom
isaac/remove-cachetools
Open

refactor: replace cachetools with stdlib OrderedDict-based LRUCache#36
isaacbmiller wants to merge 1 commit intomainfrom
isaac/remove-cachetools

Conversation

@isaacbmiller
Copy link
Copy Markdown

Summary

Remove cachetools as a direct dependency by replacing cachetools.LRUCache with a ~30-line OrderedDict subclass.

Changes

  • dspy/clients/cache.py: Added LRUCache(OrderedDict) class covering maxsize, get/set/contains/clear, LRU eviction via move_to_end, and cloudpickle serialization via __reduce__.
  • tests/clients/test_cache.py: Updated import from cachetools to dspy.clients.cache.
  • pyproject.toml: Removed cachetools>=5.5.0 from dependencies.

Notes

  • Thread safety is handled by the existing self._lock = threading.RLock() in the Cache class, not in LRUCache itself.
  • All 21 cache tests pass, including save/load via cloudpickle.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 7, 2026

Greptile Summary

This PR removes the cachetools direct dependency by replacing cachetools.LRUCache with a ~30-line OrderedDict subclass that covers the exact API surface used by DSPy.

  • dspy/clients/cache.py: New LRUCache(OrderedDict) correctly implements LRU promotion via move_to_end in both __getitem__ and __setitem__, evicts least-recently-used items when maxsize is exceeded, and supports cloudpickle round-trips via a __reduce__ 5-tuple that re-inserts items into a fresh instance.
  • pyproject.toml / uv.lock: cachetools>=5.5.0 removed from direct dependencies and lock file; thread safety is unchanged, delegated to the existing RLock in Cache.
  • tests/clients/test_cache.py: Import updated to use the new in-package LRUCache; all 21 existing tests continue to exercise the same behaviors.

Confidence Score: 5/5

Safe to merge — the custom LRUCache correctly replicates the cachetools contract used by DSPy, cloudpickle serialization is preserved via reduce, and thread safety is unaffected.

The replacement implementation faithfully covers every code path the Cache class exercises: LRU promotion on read and write, front-of-dict eviction when capacity is exceeded, and pickle round-tripping. The existing 21-test suite covers memory cache get/put, save/load, and eviction. No behavioral regression is apparent.

No files require special attention.

Important Files Changed

Filename Overview
dspy/clients/cache.py Adds a custom LRUCache(OrderedDict) subclass replacing cachetools.LRUCache; implementation correctly handles get/set with LRU promotion, eviction, and cloudpickle serialization via reduce.
tests/clients/test_cache.py Updated import from cachetools.LRUCache to dspy.clients.cache.LRUCache; test coverage unchanged and all existing cases still apply to the new implementation.
pyproject.toml Removes cachetools>=5.5.0 from direct dependencies; the dependency is no longer needed.
uv.lock Lock file updated to remove cachetools package entry; consistent with pyproject.toml change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["LRUCache.__setitem__(key, value)"] --> B{key in cache?}
    B -- Yes --> C["move_to_end(key)\n(promote to MRU)"]
    C --> D["super().__setitem__(key, value)\n(update in-place)"]
    B -- No --> D
    D --> E{len > maxsize?}
    E -- Yes --> F["pop(next(iter(self)))\n(evict LRU item)"]
    F --> E
    E -- No --> G[Done]

    H["LRUCache.__getitem__(key)"] --> I["move_to_end(key)\n(promote to MRU)"]
    I --> J["return super().__getitem__(key)"]

    K["LRUCache.__reduce__()"] --> L["return (LRUCache, (maxsize,), None, None, iter(items))"]
    L --> M["Pickle reconstructs:\nLRUCache(maxsize)\nthen obj[k]=v for each item"]
Loading

Reviews (3): Last reviewed commit: "refactor: replace cachetools with stdlib..." | Re-trigger Greptile

Comment thread pyproject.toml
Comment thread dspy/clients/cache.py Outdated
@isaacbmiller isaacbmiller force-pushed the isaac/remove-cachetools branch from 9eb2f08 to 99d30c7 Compare May 7, 2026 19:44
Replace cachetools.LRUCache with a ~30-line OrderedDict subclass that
covers the subset used by DSPy: maxsize, get/set/contains/clear, and
cloudpickle serialization via __reduce__.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@isaacbmiller isaacbmiller force-pushed the isaac/remove-cachetools branch from 99d30c7 to 5208ae1 Compare May 7, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant