Skip to content

Commit

Permalink
Merge branch 'release/1.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
xeroc committed Oct 28, 2020
2 parents cc3dbce + 523dcfe commit 16596c7
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 39 deletions.
6 changes: 6 additions & 0 deletions .changes/1.5.0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"description": "Make use of ExpiringDict to limit memory usage in cache",
"type": "minor"
}
]
2 changes: 1 addition & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
811 Fabian Schuh <[email protected]>
817 Fabian Schuh <[email protected]>
167 Fabian Schuh <[email protected]>
104 Vladimir Kamarzin <[email protected]>
100 Fabian Schuh <[email protected]>
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog
Note: version releases in the 0.x.y range may introduce breaking changes.

## 1.5.0

- minor: Make use of ExpiringDict to limit memory usage in cache

## 1.4.4

- patch: Make the caching key explicit
Expand Down
54 changes: 17 additions & 37 deletions graphenecommon/objectcache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from abc import ABC, abstractmethod, abstractproperty
from expiringdict import ExpiringDict


class ObjectCacheInterface:
Expand Down Expand Up @@ -34,54 +35,33 @@ def __str__(self):
)


class ObjectCacheInMemory(ObjectCacheInterface, dict):
class ObjectCacheInMemory(ExpiringDict, ObjectCacheInterface):
""" This class implements an object/dict cache that comes with an
expiration. Expired items are removed from the cache.
expiration. Expired items are removed from the cache. The cache has a
max_length.
"""

def __init__(self, initial_data={}, default_expiration=60, no_overwrite=False):
def __init__(
self,
initial_data={},
default_expiration=60,
no_overwrite=False,
max_length=1000,
):
dict.__init__(self, initial_data)

# Expiration
self.set_expiration(default_expiration)
self.default_expiration = default_expiration

# This allows nicer testing
self.no_overwrite = no_overwrite

def __setitem__(self, key, value):
if key in self and not self.no_overwrite:
del self[key]
elif key in self and self.no_overwrite:
return
data = {
"expires": datetime.utcnow() + timedelta(seconds=self.default_expiration),
"data": value,
}
dict.__setitem__(self, key, data)

def __getitem__(self, key):
""" Returns an element from the cache if available, else returns
the value provided as default or None
"""
if key in self:
value = dict.__getitem__(self, key)
return value["data"]
# Number of items to store (in total)
self._max_items = max_length

def get(self, key, default_value=None):
if key in self:
return self[key]
else:
return default_value

def __contains__(self, key):
if dict.__contains__(self, key):
value = dict.__getitem__(self, key)
if datetime.utcnow() < value["expires"]:
return True
else:
# Remove from cache
dict.pop(self, key, None)
return False
ExpiringDict.__init__(
self, max_len=self._max_items, max_age_seconds=self.default_expiration
)

def set_expiration(self, expiration):
""" Set new default expiration time in seconds (default: 10s)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ scrypt>=0.8.13,<0.9
websockets>=8.1,<9
aiohttp>=3.5.4,<4
asyncinit>=0.2.4,<0.3
expiringdict
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import setup

__version__ = "1.4.4"
__version__ = "1.5.0"
URL = "https://github.com/xeroc/python-graphenelib"

setup(
Expand Down
14 changes: 14 additions & 0 deletions tests/test_objectcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ def test_cache(self):
# Get
self.assertEqual(cache.get("foo", "New"), "New")

def test_max_length_cache(self):
cache = ObjectCacheInMemory(default_expiration=1, max_length=10)
self.assertEqual(str(cache), "ObjectCacheInMemory(default_expiration=1)")

for num in range(0, 10):
cache[str(num)] = "bar"

self.assertEqual(len(cache), 10)

for num in range(11, 50):
cache[str(num)] = "bar"

self.assertEqual(len(cache), 10)

def test_cache_list(self):
cache = ObjectCacheInMemory(default_expiration=1)
self.assertEqual(str(cache), "ObjectCacheInMemory(default_expiration=1)")
Expand Down

0 comments on commit 16596c7

Please sign in to comment.