Interface improvement for cached object (inkl. example adaption)

This commit is contained in:
Dirk Alders 2024-09-15 18:58:42 +02:00
parent 8eeb7657be
commit 529f4100b6
3 changed files with 108 additions and 126 deletions

View File

@ -28,7 +28,6 @@ import json
import logging import logging
import os import os
import pickle import pickle
import sys
import time import time
try: try:
@ -53,6 +52,9 @@ class property_cache_pickle(object):
:param cache_filename: File name, where the properties are stored as cache :param cache_filename: File name, where the properties are stored as cache
:type cache_filename: str :type cache_filename: str
:param load_all_on_init: Optionally init behaviour control parameter. True will load all available properties from source on init, False not. :param load_all_on_init: Optionally init behaviour control parameter. True will load all available properties from source on init, False not.
:param max_age: The maximum age of the cache object, after that time the source will be used for getting information.
:type max_age: int
:param store_on_get: Parameter to enable / disable cache storage on get. If you disble data storage, you need to update and store the cache manually
.. note:: source_instance needs to have at least the following methods: uid(), keys(), data_version(), get() .. note:: source_instance needs to have at least the following methods: uid(), keys(), data_version(), get()
@ -94,6 +96,8 @@ class property_cache_pickle(object):
self._callback_on_data_storage = callback_on_data_storage self._callback_on_data_storage = callback_on_data_storage
self._max_age = max_age self._max_age = max_age
self._store_on_get = store_on_get self._store_on_get = store_on_get
#
self._source_get_keys = []
self._cached_props = None self._cached_props = None
def get(self, key, default=None): def get(self, key, default=None):
@ -104,7 +108,7 @@ class property_cache_pickle(object):
:param default: value to be returned, if key does not exists. :param default: value to be returned, if key does not exists.
:returns: value for a given key or default value. :returns: value for a given key or default value.
""" """
if key in self.keys(): if key in self.keys() and key not in self._source_get_keys:
if self._cached_props is None: if self._cached_props is None:
self._init_cache() self._init_cache()
if self._max_age is None: if self._max_age is None:
@ -198,10 +202,11 @@ class property_cache_pickle(object):
def _load_source(self): def _load_source(self):
if self._cached_props is None: if self._cached_props is None:
self._init_cache() self._init_cache()
logger.debug('%s Loading all data from source - %s', self.LOG_PREFIX, repr(self._source_instance.keys())) logger.debug('%s Loading all data from source - %s', self.LOG_PREFIX, repr(self.keys()))
for key in self._source_instance.keys(): for key in self.keys():
self._cached_props[self.DATA_TAG][self._key_filter(key)] = self._source_instance.get(key) if key not in self._source_get_keys:
self._cached_props[self.AGE_TAG][self._key_filter(key)] = int(time.time()) self._cached_props[self.DATA_TAG][self._key_filter(key)] = self._source_instance.get(key)
self._cached_props[self.AGE_TAG][self._key_filter(key)] = int(time.time())
def _save_cache(self): def _save_cache(self):
with open(self._cache_filename, 'wb') as fh: with open(self._cache_filename, 'wb') as fh:
@ -216,6 +221,18 @@ class property_cache_pickle(object):
else: else:
return self._cached_props.get(self.UID_TAG, None) return self._cached_props.get(self.UID_TAG, None)
def add_source_get_keys(self, keys):
if type(keys) in [list, tuple]:
self._source_get_keys.extend(keys)
else:
self._source_get_keys.append(keys)
def __getattribute__(self, name):
try:
return super().__getattribute__(name)
except AttributeError:
return getattr(self._source_instance, name)
class property_cache_json(property_cache_pickle): class property_cache_json(property_cache_pickle):
""" """
@ -226,6 +243,9 @@ class property_cache_json(property_cache_pickle):
:param cache_filename: File name, where the properties are stored as cache :param cache_filename: File name, where the properties are stored as cache
:type cache_filename: str :type cache_filename: str
:param load_all_on_init: Optionally init behaviour control parameter. True will load all available properties from source on init, False not. :param load_all_on_init: Optionally init behaviour control parameter. True will load all available properties from source on init, False not.
:param max_age: The maximum age of the cache object, after that time the source will be used for getting information.
:type max_age: int
:param store_on_get: Parameter to enable / disable cache storage on get. If you disble data storage, you need to update and store the cache manually
.. warning:: .. warning::
* This class uses json. You should **only** use keys of type string! * This class uses json. You should **only** use keys of type string!

View File

@ -2,63 +2,32 @@
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
import sys import sys
import time
sys.path.append('../..') sys.path.append('../..')
import caching
import report import report
import caching
import time
report.stdoutLoggingConfigure(log_name_lvl=[('root', 'DEBUG'), ]) report.stdoutLoggingConfigure(log_name_lvl=[('root', 'DEBUG'), ])
class test_slow_data(object): class test_slow_data(object):
_ONE = '1' DATA_VERSION = 0.1
_TWO = '2' KEY_ONE = '1'
_THREE = '_property_cache_data_version_' KEY_TWO = '2'
_FOUR = '_property_cache_uid_' KEY_THREE = 'three'
_FIVE = '__property_cache_uid_' KEY_FOUR = 'four'
KEYS = [_ONE, _TWO, _THREE, _FOUR, _FIVE] KEY_FIVE = 'five'
VERS = 0.1 KEYS = [KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, KEY_FIVE]
def data_version(self): def data_version(self):
return self.VERS return self.DATA_VERSION
def one(self):
return self.get(self._ONE)
def two(self):
return self.get(self._TWO)
def three(self):
return self.get(self._THREE)
def four(self):
return self.get(self._FOUR)
def five(self):
return self.get(self._FIVE)
def get(self, key, default=None): def get(self, key, default=None):
def print_n_sleep(k): try:
sys.stdout.write('slow get executed for %s\n' % k) return getattr(self, f'__{key}__')()
time.sleep(3) except AttributeError:
if key == self._ONE: return default
print_n_sleep(key)
return 'one'
if key == self._TWO:
print_n_sleep(key)
return 'two'
if key == self._THREE:
print_n_sleep(key)
return 'three'
if key == self._FOUR:
print_n_sleep(key)
return 'four'
if key == self._FIVE:
print_n_sleep(key)
return 'five'
return default
def keys(self): def keys(self):
return self.KEYS return self.KEYS
@ -66,23 +35,35 @@ class test_slow_data(object):
def uid(self): def uid(self):
return None return None
def print_n_sleep(self, k):
sys.stdout.write('slow get executed for %s\n' % k)
time.sleep(3)
class tsd_cache_json(test_slow_data): def __1__(self):
def __init__(self, *args, **kwargs): self.print_n_sleep("__1__")
test_slow_data.__init__(self, *args, **kwargs) return 'one'
self._cached_data = caching.property_cache_json(test_slow_data(*args, **kwargs), 'cache.json', load_all_on_init=False)
def two(self): def __2__(self):
return test_slow_data.get(self, self._TWO) self.print_n_sleep("__2__")
return 'two'
def get(self, key, default=None): def __three__(self):
return self._cached_data.get(key, default) self.print_n_sleep("__three__")
return 'three'
def __four__(self):
self.print_n_sleep("__four__")
return 'four'
def __five__(self):
self.print_n_sleep("__five__")
return 'five'
data = tsd_cache_json() if __name__ == "__main__":
print('Testing property_cache (json):\\n--------------------------------') data = caching.property_cache_json(test_slow_data(), 'cache.json')
print(data.one()) data.add_source_get_keys(data.KEY_THREE)
print(data.two()) print('Testing property_cache (json):\\n--------------------------------')
print(data.three()) for key in data.keys():
print(data.four()) print(data.get(key))
print(data.five())

View File

@ -2,63 +2,32 @@
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
import sys import sys
import time
sys.path.append('../..') sys.path.append('../..')
import caching
import report import report
import caching
import time
report.stdoutLoggingConfigure(log_name_lvl=[('root', 'DEBUG'), ]) report.stdoutLoggingConfigure(log_name_lvl=[('root', 'DEBUG'), ])
class test_slow_data(object): class test_slow_data(object):
_ONE = '1' DATA_VERSION = 0.1
_TWO = '2' KEY_ONE = '1'
_THREE = '_property_cache_data_version_' KEY_TWO = '2'
_FOUR = '_property_cache_uid_' KEY_THREE = 'three'
_FIVE = '__property_cache_uid_' KEY_FOUR = 'four'
KEYS = [_ONE, _TWO, _THREE, _FOUR, _FIVE] KEY_FIVE = 'five'
VERS = 0.1 KEYS = [KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, KEY_FIVE]
def data_version(self): def data_version(self):
return self.VERS return self.DATA_VERSION
def one(self):
return self.get(self._ONE)
def two(self):
return self.get(self._TWO)
def three(self):
return self.get(self._THREE)
def four(self):
return self.get(self._FOUR)
def five(self):
return self.get(self._FIVE)
def get(self, key, default=None): def get(self, key, default=None):
def print_n_sleep(k): try:
sys.stdout.write('slow get executed for %s\n' % k) return getattr(self, f'__{key}__')()
time.sleep(3) except AttributeError:
if key == self._ONE: return default
print_n_sleep(key)
return 'one'
if key == self._TWO:
print_n_sleep(key)
return 'two'
if key == self._THREE:
print_n_sleep(key)
return 'three'
if key == self._FOUR:
print_n_sleep(key)
return 'four'
if key == self._FIVE:
print_n_sleep(key)
return 'five'
return default
def keys(self): def keys(self):
return self.KEYS return self.KEYS
@ -66,23 +35,35 @@ class test_slow_data(object):
def uid(self): def uid(self):
return None return None
def print_n_sleep(self, k):
sys.stdout.write('slow get executed for %s\n' % k)
time.sleep(3)
class tsd_cache_pickle(test_slow_data): def __1__(self):
def __init__(self, *args, **kwargs): self.print_n_sleep("__1__")
test_slow_data.__init__(self, *args, **kwargs) return 'one'
self._cached_data = caching.property_cache_pickle(test_slow_data(*args, **kwargs), 'cache.pickle', load_all_on_init=False)
def two(self): def __2__(self):
return test_slow_data.get(self, self._TWO) self.print_n_sleep("__2__")
return 'two'
def get(self, key, default=None): def __three__(self):
return self._cached_data.get(key, default) self.print_n_sleep("__three__")
return 'three'
def __four__(self):
self.print_n_sleep("__four__")
return 'four'
def __five__(self):
self.print_n_sleep("__five__")
return 'five'
data = tsd_cache_pickle() if __name__ == "__main__":
print('Testing property_cache (pickle):\\n--------------------------------') data = caching.property_cache_pickle(test_slow_data(), 'cache.pickle')
print(data.one()) data.add_source_get_keys(data.KEY_THREE)
print(data.two()) print('Testing property_cache (pickle):\\n--------------------------------')
print(data.three()) for key in data.keys():
print(data.four()) print(data.get(key))
print(data.five())