#!/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)