repoze / repoze.lru

Tiny LRU cache
http://pypi.python.org/pypi/repoze.lru/
Other
102 stars 25 forks source link

Consider deep copy for put/get #28

Open mbeckersys opened 7 years ago

mbeckersys commented 7 years ago

Hi,

When putting or getting mutable items to/from cache, the value should be a deep copy. Otherwise the cache contents can be modified implicitly, which produces a behavior that I find quiet unintuitive for a cache.

Here is an example:

from repoze.lru import LRUCache
cache = LRUCache(10)

# place one item in cache:
val = {"hallo": 1}
cache.put("world", val)

# implicit modification of cache:
val["new"] = 2

# item returned from cache
item = cache.get("world")
print str(item) # prints {"hallo": 1, "new": 2}, but should be {"hallo": 1}

item['third'] = 3
print cache.get("world") # prints {"hallo": 1, "new": 2}, but still should be {"hallo": 1}

The items on the cache should never change after put, unless we update them explicitly. This happens for all mutable types (dicts, lists, ...).

I cannot think of a case where someone would want to save references in the cache, apart for performance reasons. I suggest to take a deep copy by default, and add an optional parameter to allow reverting to the current behavior if somebody really needs that (and knows what this is doing).

erdnaxeli commented 6 years ago

The LRU cache from the standard lib does the same:

>>> from functools import lru_cache
>>> 
>>> @lru_cache(3)
... def test(a):
...     return {'a': a}
... 
>>> x = test(3)
>>> x
{'a': 3}
>>> x['a'] = 42
>>> x
{'a': 42}
>>> y = test(3)
>>> y
{'a': 42}