Merge pull request #8 from MichaelRazum/master

Added RecalculationNeeded Exception
This commit is contained in:
Shay Palachy
2018-10-17 13:53:11 +03:00
committed by GitHub
3 changed files with 55 additions and 7 deletions
+9 -5
View File
@@ -30,7 +30,7 @@ except ImportError: # we're in python 2.x
from concurrent.futures import ThreadPoolExecutor
from .pickle_core import _PickleCore
from .mongo_core import _MongoCore
from .mongo_core import _MongoCore, RecalculationNeeded
@@ -82,8 +82,6 @@ def _calc_entry(core, key, func, args, kwds):
finally:
core.mark_entry_not_calculated(key)
def cachier(stale_after=None, next_time=False, pickle_reload=True,
mongetter=None):
"""A persistent, stale-free memoization decorator.
@@ -156,7 +154,10 @@ def cachier(stale_after=None, next_time=False, pickle_reload=True,
_print('Returning stale.')
return entry['value'] # return stale val
_print('Already calc. Waiting on change.')
return core.wait_on_entry_calc(key)
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
return _calc_entry(core, key, func, args, kwds)
if next_time:
_print('Async calc and return stale')
try:
@@ -173,7 +174,10 @@ def cachier(stale_after=None, next_time=False, pickle_reload=True,
return entry['value']
if entry['being_calculated']:
_print('No value but being calculated. Waiting.')
return core.wait_on_entry_calc(key)
try:
return core.wait_on_entry_calc(key)
except RecalculationNeeded:
return _calc_entry(core, key, func, args, kwds)
_print('No entry found. No current calc. Calling like a boss.')
return _calc_entry(core, key, func, args, kwds)
+4
View File
@@ -28,6 +28,8 @@ from .base_core import _BaseCore
MONGO_SLEEP_DURATION_IN_SEC = 1
class RecalculationNeeded(Exception):
pass
class _MongoCore(_BaseCore):
@@ -126,6 +128,8 @@ class _MongoCore(_BaseCore):
while True:
time.sleep(MONGO_SLEEP_DURATION_IN_SEC)
key, entry = self.get_entry_by_key(key)
if entry is None:
raise RecalculationNeeded()
if entry is not None and not entry['being_calculated']:
return entry['value']
+42 -2
View File
@@ -1,5 +1,6 @@
"""Testing the MongoDB core of cachier."""
import datetime
import sys
from random import random
from datetime import timedelta
@@ -15,8 +16,7 @@ from pymongo.mongo_client import MongoClient
from pymongo.errors import OperationFailure
from cachier import cachier
from cachier.mongo_core import _MongoCore
from cachier.mongo_core import _MongoCore, RecalculationNeeded
_TEST_HOST = 'ds119508.mlab.com'
_TEST_PORT = 19508
@@ -163,3 +163,43 @@ def test_mongo_write_failure():
def test_mongo_clear_being_calculated():
"""Testing MongoDB core clear_being_calculated."""
_func_w_bad_mongo.clear_being_calculated()
def test_stalled_mongo_db_cache():
@cachier(mongetter=_test_mongetter)
def _stalled_func():
return 1
core = _MongoCore(_test_mongetter, None, False)
core.set_func(_stalled_func)
core.clear_cache()
with pytest.raises(RecalculationNeeded):
core.wait_on_entry_calc(key=None)
def test_stalled_mong_db_core(monkeypatch):
def mock_get_entry(self, args, kwargs):
return "key", {'being_calculated': True}
def mock_get_entry_by_key(self, key):
return "key", None
monkeypatch.setattr("cachier.mongo_core._MongoCore.get_entry", mock_get_entry )
monkeypatch.setattr("cachier.mongo_core._MongoCore.get_entry_by_key", mock_get_entry_by_key )
@cachier(mongetter=_test_mongetter)
def _stalled_func():
return 1
res = _stalled_func()
assert res == 1
def mock_get_entry_2(self, args, kwargs):
return "key", {'being_calculated': True,
"value": 1,
"time": datetime.datetime.now() - datetime.timedelta(seconds=10)}
monkeypatch.setattr("cachier.mongo_core._MongoCore.get_entry", mock_get_entry_2 )
stale_after = datetime.timedelta(seconds=1)
@cachier(mongetter=_test_mongetter,stale_after=stale_after)
def _stalled_func_2():
""" Testing stalled function"""
return 2
res = _stalled_func_2()
assert res == 2