{"id":2282,"library":"shelved-cache","title":"Shelved Cache","description":"Shelved Cache (version 0.5.0) is a Python library that provides a persistent cache implementation for `cachetools` objects, storing entries to disk using Python's `shelve` module. It allows for fast, persistent caching of function results across program executions. The library is actively maintained, integrating with standard Python caching mechanisms.","status":"active","version":"0.5.0","language":"en","source_language":"en","source_url":"https://github.com/mariushelf/shelved_cache","tags":["cache","persistence","disk-cache","shelve","cachetools","decorator"],"install":[{"cmd":"pip install shelved-cache","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Provides the in-memory cache implementations (e.g., LRUCache) that Shelved Cache wraps for persistence. Version 0.5.0 supports cachetools ^6.0.0.","package":"cachetools","optional":false}],"imports":[{"symbol":"PersistentCache","correct":"from shelved_cache import PersistentCache"},{"note":"For asynchronous function caching, available in the 'decorators' submodule.","symbol":"AsyncPersistentCache","correct":"from shelved_cache import AsyncPersistentCache"}],"quickstart":{"code":"import cachetools\nfrom shelved_cache import PersistentCache\nfrom cachetools import LRUCache\nimport os\n\n# Define a unique cache file path\ncache_file = \"my_function_cache.db\"\n\n# --- Cleanup any previous cache files for a clean run ---\n# shelve can create multiple files (.db, .bak, .dir, .dat)\nfor ext in [\"\", \".bak\", \".dir\", \".dat\"]:\n    if os.path.exists(cache_file + ext):\n        os.remove(cache_file + ext)\n\n# Initialize a persistent LRU cache, linking it to a file\npc_for_square = PersistentCache(LRUCache, cache_file, maxsize=100)\n\n@cachetools.cached(pc_for_square)\ndef square(x):\n    print(f\"Calculating square for {x}...\")\n    return x * x\n\n# First call: calculation happens\nresult1 = square(5)\nprint(f\"First call: square(5) = {result1}\")\n\n# Second call: result is retrieved from cache (no 'Calculating...' output)\nresult2 = square(5)\nprint(f\"Second call: square(5) = {result2}\")\n\n# A different argument will trigger a new calculation and cache entry\nresult3 = square(10)\nprint(f\"Third call: square(10) = {result3}\")\n\n# It's crucial to close the persistent cache to ensure data is written to disk.\npc_for_square.close()\n\nprint(\"\\n--- Cache closed and reopened to demonstrate persistence ---\")\n\n# Simulate a new application run or process by creating a new PersistentCache instance\n# linked to the *same* file.\npc_reopened = PersistentCache(LRUCache, cache_file, maxsize=100)\n\n@cachetools.cached(pc_reopened)\ndef square_reopened(x):\n    # This should *not* print if the value was correctly persisted\n    print(f\"Calculating square (reopened) for {x}...\")\n    return x * x\n\n# This call should retrieve from the persisted cache without recalculating\nresult_reopened = square_reopened(5)\nprint(f\"Reopened call: square_reopened(5) = {result_reopened}\")\n\npc_reopened.close()\n\n# --- Final cleanup of created cache files ---\nfor ext in [\"\", \".bak\", \".dir\", \".dat\"]:\n    if os.path.exists(cache_file + ext):\n        os.remove(cache_file + ext)\n","lang":"python","description":"This quickstart demonstrates how to use `shelved-cache` to make a `cachetools.LRUCache` persistent. It shows a function being decorated, calls it multiple times to demonstrate caching, explicitly closes the cache, and then reopens it in a simulated new process to prove persistence. Ensure proper cleanup of the cache files created."},"warnings":[{"fix":"Upgrade Python to version 3.10.0 or higher.","message":"Version 0.5.0 drops support for Python 3.9. Older versions (0.4.0) also dropped support for Python 3.7 and 3.8. Users should ensure they are running on Python 3.10 or newer.","severity":"breaking","affected_versions":"0.4.0, 0.5.0"},{"fix":"Upgrade `cachetools` to version 6.0.0 or higher: `pip install \"cachetools>=6.0.0,<7.0.0\"`.","message":"Version 0.5.0 requires `cachetools` version `^6.0.0`. Older versions of `cachetools` are no longer supported.","severity":"breaking","affected_versions":"0.5.0"},{"fix":"For each decorated function, create a new `PersistentCache(cache_type, 'unique_filename.db', ...)` instance.","message":"When decorating multiple functions with `shelved-cache`, each function MUST use a *separate* `PersistentCache` instance and a *different* file name for its persistence store. Reusing the same file for multiple caches will lead to errors.","severity":"gotcha","affected_versions":"All"},{"fix":"For multi-process applications requiring concurrent writes, consider external synchronization mechanisms (e.g., file locks, message queues) or an alternative cache backend (e.g., Redis, dedicated database).","_review":true,"message":"The underlying `shelve` module, which `shelved-cache` uses, does not natively support concurrent read/write access from multiple processes. While multiple simultaneous reads are safe, if one process has the shelf open for writing, no other process should have it open for reading or writing without explicit external locking. This can lead to data corruption or deadlocks in multi-process environments.","severity":"gotcha","_review_reason":"While shelved-cache abstracts shelve, this is a fundamental limitation that might not be fully mitigated by the library itself without explicit mention. The source doesn't explicitly mention shelved_cache solving this.","affected_versions":"All"},{"fix":"If encountering issues, consider using a different Python version, operating system, or an alternative caching solution on Windows.","message":"Users on Windows with Python 3.13 and above might encounter permission errors or unexpected behavior. While earlier Windows issues were addressed, this specific combination has been noted as potentially problematic.","severity":"gotcha","affected_versions":"0.4.0, 0.5.0 (with Python 3.13+ on Windows)"}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}