#!/usr/bin/python # -*- coding: utf-8 -*- # Hive Appier Framework # Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Appier Framework. # # Hive Appier Framework is free software: you can redistribute it and/or modify # it under the terms of the Apache License as published by the Apache # Foundation, either version 2.0 of the License, or (at your option) any # later version. # # Hive Appier Framework is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # Apache License for more details. # # You should have received a copy of the Apache License along with # Hive Appier Framework. If not, see . __author__ = "João Magalhães " """ The author(s) of the module """ __copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" """ The license for the module """ import os import time import pickle import shutil from . import util from . import legacy from . import common from . import config from . import redisdb from . import component class Cache(component.Component): def __init__(self, name="cache", owner=None, *args, **kwargs): component.Component.__init__(self, name=name, owner=owner, *args, **kwargs) load = kwargs.pop("load", True) if load: self.load(*args, **kwargs) def __len__(self): return self.length() def __getitem__(self, key): return self.get_item(key) def __setitem__(self, key, value): return self.set_item(key, value) def __delitem__(self, key): return self.delete_item(key) def __contains__(self, item): return self.contains(item) def __nonzero__(self): return True def __bool__(self): return True @classmethod def new(cls, *args, **kwargs): return cls(*args, **kwargs) def length(self): return 0 def clear(self): pass def get(self, key, default=None): try: return self.get_item(key) except KeyError: return default def get_item(self, key): raise KeyError("not found") def set(self, *args, **kwargs): return self.set_item(*args, **kwargs) def set_item(self, key, value, expires=None, timeout=None): self.mark() def delete_item(self, key): self.mark() def contains(self, key): try: self.get_item(key) except KeyError: return False return True def build_value(self, value, expires, timeout): if timeout: expires = time.time() + timeout return (value, expires) class MemoryCache(Cache): def __init__(self, name="memory", owner=None, *args, **kwargs): Cache.__init__(self, name=name, owner=owner, *args, **kwargs) def length(self): return self._data.__len__() def clear(self): self._data.clear() def get_item(self, key): value, expires = self._data.__getitem__(key) if not expires == None and expires < time.time(): self.delete_item(key) return self.get_item(key) return value def set_item(self, key, value, expires=None, timeout=None): value = self.build_value(value, expires, timeout) return self._data.__setitem__(key, value) def delete_item(self, key): return self._data.__delitem__(key) def _load(self, *args, **kwargs): Cache._load(self, *args, **kwargs) self._data = dict() def _unload(self, *args, **kwargs): Cache._unload(self, *args, **kwargs) self._data = None class FileCache(Cache): def __init__(self, name="file", owner=None, *args, **kwargs): Cache.__init__(self, name=name, owner=owner, *args, **kwargs) def length(self): if not os.path.exists(self.base_path): return 0 return len(os.listdir(self.base_path)) def clear(self): if not os.path.exists(self.base_path): return shutil.rmtree(self.base_path, ignore_errors=True) def get_item(self, key): file_path = os.path.join(self.base_path, key) if not os.path.exists(file_path): raise KeyError("not found") if not os.path.exists(file_path + ".expires"): raise KeyError("not found") expires = self._read_file(file_path + ".expires") expires = int(expires) if expires else None if not expires == None and expires < time.time(): self.delete_item(key) return self.get_item(key) value = self._read_file(file_path) return value def set_item(self, key, value, expires=None, timeout=None): file_path = os.path.join(self.base_path, key) value, expires = self.build_value(value, expires, timeout) expires = str(int(expires)) if expires else None expires_s = legacy.bytes(expires, force=True) if expires else b"" self._write_file(file_path, value) self._write_file(file_path + ".expires", expires_s) def delete_item(self, key): file_path = os.path.join(self.base_path, key) os.remove(file_path) os.remove(file_path + ".expires") def _load(self, *args, **kwargs): Cache._load(self, *args, **kwargs) self.base_path = kwargs.pop("base_path", None) self._ensure_path() def _read_file(self, file_path): file = open(file_path, "rb") try: data = file.read() finally: file.close() return data def _write_file(self, file_path, data): self._ensure_exists() file = open(file_path, "wb") try: file.write(data) finally: file.close() def _ensure_exists(self): if os.path.exists(self.base_path): return os.makedirs(self.base_path) def _ensure_path(self): if self.base_path: return app_path = common.base().get_base_path() util.verify(not app_path == None, message="No app path available") cache_path = os.path.join(app_path, "cache") cache_path = config.conf("CACHE_PATH", cache_path) cache_path = os.path.expanduser(cache_path) cache_path = os.path.abspath(cache_path) cache_path = os.path.normpath(cache_path) self.base_path = cache_path class RedisCache(Cache): def __init__(self, name="redis", owner=None, *args, **kwargs): Cache.__init__(self, name=name, owner=owner, *args, **kwargs) def length(self): if self.hash: return self._redis.hlen(self.key) else: return len(self._redis.keys()) def clear(self): if self.hash: self._redis.delete(self.key) else: self._redis.flushdb() def get_item(self, key): if self.hash: return self._get_item_hash(key) else: return self._get_item(key) def set_item(self, key, value, expires=None, timeout=None): if self.hash: return self._set_item_hash(key, value, expires=expires, timeout=timeout) else: return self._set_item(key, value, expires=expires, timeout=timeout) def delete_item(self, key): if self.hash: self._redis.hdel(self.key, key) else: self._redis.delete(key) def _load(self, *args, **kwargs): Cache._load(self, *args, **kwargs) self.hash = kwargs.pop("hash", False) self._redis = redisdb.get_connection() self._redis.ping() def _unload(self, *args, **kwargs): Cache._unload(self, *args, **kwargs) self._redis = None def _get_item(self, key): if not self._redis.exists(key): raise KeyError("not found") return self._redis.get(key) def _get_item_hash(self, key): if not self._redis.hexists(self.key, key): raise KeyError("not found") return self._redis.hget(self.key, key) def _set_item(self, key, value, expires=None, timeout=None): self._redis.set(key, value) if expires: timeout = expires - time.time() if timeout and timeout > 0: self._redis.expire(key, int(timeout)) elif timeout: self._redis.delete(key) def _set_item_hash(self, key, value, expires=None, timeout=None): self._redis.hset(self.key, key, value) if expires: timeout = expires - time.time() if timeout and timeout > 0: self._redis.expire(self.key, int(timeout)) elif timeout: self._redis.hdel(self.key, key) @property def key(self, prefix="cache"): suffix = self.owner.name_i if self.owner else "global" return prefix + ":" + suffix class SerializedCache(object): SERIALIZER = pickle """ The serializer to be used for the values contained in the session (used on top of the class) """ def __init__(self, cache, serializer=None): cls = self.__class__ self._cache = cache self._serializer = serializer or cls.SERIALIZER def __len__(self): return self._cache.__len__() def __getitem__(self, key): return self.get_item(key) def __setitem__(self, key, value): return self.set_item(key, value) def __delitem__(self, key): return self._cache.__delitem__(key) def __contains__(self, item): return self._cache.__contains__(item) def __nonzero__(self): return self._cache.__nonzero__() def __bool__(self): return self._cache.__bool__() def __getattr__(self, name): if hasattr(self._cache, name): return getattr(self._cache, name) raise AttributeError("'%s' not found" % name) def get_item(self, key): data = self._cache.get_item(key) value = self._serializer.loads(data) return value def set_item(self, key, value, expires=None, timeout=None): data = self._serializer.dumps(value) return self._cache.set_item(key, data, expires=expires, timeout=timeout)