{"id":8981,"library":"expiring-dict","title":"Expiring Dict","description":"expiring-dict is a Python library that provides a dictionary-like object with Time-To-Live (TTL) support for its values, enabling automatic expiration of cached items. It is designed for simple caching scenarios where items need to be removed after a certain period. The library is currently active, with its latest version released in February 2025, and offers both dictionary-level and key-level expiration settings.","status":"active","version":"1.1.2","language":"en","source_language":"en","source_url":"https://github.com/dparker2/py-expiring-dict","tags":["caching","dictionary","ttl","expiration","cache","in-memory"],"install":[{"cmd":"pip install expiring-dict","lang":"bash","label":"Install using pip"}],"dependencies":[],"imports":[{"symbol":"ExpiringDict","correct":"from expiring_dict import ExpiringDict"}],"quickstart":{"code":"from expiring_dict import ExpiringDict\nimport time\n\n# Dictionary-level TTL: items expire after 1 second\ncache_dict_level = ExpiringDict(max_age_seconds=1)\ncache_dict_level[\"my_key\"] = \"my_value\"\nprint(f\"Initial value (dict-level): {cache_dict_level.get('my_key')}\")\ntime.sleep(1.5)\nprint(f\"Value after 1.5 seconds (dict-level): {cache_dict_level.get('my_key')}\") # Should be None\n\n# Key-level TTL: individual keys expire after specified seconds\ncache_key_level = ExpiringDict()\ncache_key_level[\"persistent_key\"] = \"this never expires by default\"\ncache_key_level.ttl(\"expiring_key\", \"this expires in 1 second\", 1)\n\nprint(f\"\\nPersistent value: {cache_key_level.get('persistent_key')}\")\nprint(f\"Expiring value (initial): {cache_key_level.get('expiring_key')}\")\ntime.sleep(1.5)\nprint(f\"Persistent value (after 1.5s): {cache_key_level.get('persistent_key')}\")\nprint(f\"Expiring value (after 1.5s): {cache_key_level.get('expiring_key')}\") # Should be None","lang":"python","description":"Demonstrates creating an ExpiringDict with a global TTL and setting individual TTLs for specific keys. Accessing an expired key returns None, and the key is removed."},"warnings":[{"fix":"Always access items with `cache.get(key)` or `cache[key]` to trigger expiration. If an accurate length or full cleanup is needed, consider manually calling `cache.cleanup()` (if available, check documentation for `expiring-dict`) or iterating through items with a `.get()` call on each.","message":"Iteration (e.g., `for key in cache` or `cache.keys()`) and checking length (`len(cache)`) do not automatically remove expired items. Expiration only occurs when an item is accessed (e.g., `cache['key']` or `cache.get('key')`). This can lead to seemingly 'stale' or inaccurate views of the dictionary content if not handled explicitly.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Avoid pickling ExpiringDict instances directly. If serialization is required, extract the raw data (e.g., `dict(cache.items_with_timestamp())` if applicable) and reconstruct the ExpiringDict after deserialization. Alternatively, consider a different caching solution if direct pickling of the cache object is a critical requirement.","message":"ExpiringDict objects may not be reliably picklable due to internal threading locks used for thread safety. Attempting to pickle an ExpiringDict can result in a `TypeError` related to unpicklable lock objects, making it difficult to serialize and deserialize the cache.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Initialize `ExpiringDict(max_age_seconds=None)` if you plan to manage expiration solely on a per-key basis. For keys with specific TTLs, use `cache.ttl(key, value, custom_age)`. Keys set via `cache[key] = value` will inherit the dictionary's `max_age_seconds` if set, or have no expiration if `max_age_seconds` is `None`.","message":"Setting `max_age_seconds` for the `ExpiringDict` applies to *all* keys in the dictionary by default. If you intend for some keys to have no expiration or a different expiration, you must explicitly use the `cache.ttl(key, value, age)` method or ensure `max_age_seconds` is `None` during initialization if you only want key-level TTLs.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Always use `value = cache.get('key')` to safely retrieve values. This method returns `None` (or a specified default) if the key does not exist or has expired, preventing a `KeyError`.","cause":"Attempting to access an expired key using `cache['key']` instead of `cache.get('key')`. When a key expires, it's effectively removed, so direct access will raise a KeyError.","error":"KeyError: 'some_key'"},{"fix":"Do not attempt to pickle `ExpiringDict` objects directly. If you need to persist the cached data, extract the key-value pairs (and their timestamps if needed) into a standard dictionary or list of tuples, then pickle that data. Reconstruct the `ExpiringDict` from this data upon deserialization.","cause":"You are attempting to serialize an `ExpiringDict` instance using a module like `pickle` or `dill`. `ExpiringDict` uses internal threading locks for thread-safety, which are generally not picklable.","error":"TypeError: cannot pickle '_thread.lock' object"}]}