summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--slixmpp/util/__init__.py2
-rw-r--r--slixmpp/util/cache.py105
2 files changed, 107 insertions, 0 deletions
diff --git a/slixmpp/util/__init__.py b/slixmpp/util/__init__.py
index 8f70b2bb..ecf118d7 100644
--- a/slixmpp/util/__init__.py
+++ b/slixmpp/util/__init__.py
@@ -13,3 +13,5 @@
from slixmpp.util.misc_ops import bytes, unicode, hashes, hash, \
num_to_bytes, bytes_to_num, quote, \
XOR
+from slixmpp.util.cache import MemoryCache, MemoryPerJidCache, \
+ FileSystemCache, FileSystemPerJidCache
diff --git a/slixmpp/util/cache.py b/slixmpp/util/cache.py
new file mode 100644
index 00000000..059eca5b
--- /dev/null
+++ b/slixmpp/util/cache.py
@@ -0,0 +1,105 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2018 Emmanuel Gil Peyrot
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+import os
+import logging
+
+log = logging.getLogger(__name__)
+
+class Cache:
+ def retrieve(self, key):
+ raise NotImplementedError
+
+ def store(self, key, value):
+ raise NotImplementedError
+
+class PerJidCache:
+ def retrieve_by_jid(self, jid, key):
+ raise NotImplementedError
+
+ def store_by_jid(self, jid, key, value):
+ raise NotImplementedError
+
+class MemoryCache(Cache):
+ def __init__(self):
+ self.cache = {}
+
+ def retrieve(self, key):
+ return self.cache.get(key, None)
+
+ def store(self, key, value):
+ self.cache[key] = value
+ return True
+
+class MemoryPerJidCache(PerJidCache):
+ def __init__(self):
+ self.cache = {}
+
+ def retrieve_by_jid(self, jid, key):
+ cache = self.cache.get(jid.bare, None)
+ if cache is None:
+ return None
+ return cache.get(key, None)
+
+ def store_by_jid(self, jid, key, value):
+ cache = self.cache.setdefault(jid.bare, {})
+ cache[key] = value
+ return True
+
+class FileSystemStorage:
+ def __init__(self, encode, decode, binary):
+ self.encode = encode
+ self.decode = decode
+ self.read = 'rb' if binary else 'r'
+ self.write = 'wb' if binary else 'w'
+
+ def _retrieve(self, directory, key):
+ filename = os.path.join(directory, key.replace('/', '_'))
+ try:
+ with open(filename, self.read) as cache_file:
+ return self.decode(cache_file.read())
+ except FileNotFoundError:
+ log.debug('%s not present in cache', key)
+ except OSError:
+ log.debug('Failed to read %s from cache:', key, exc_info=True)
+ return None
+
+ def _store(self, directory, key, value):
+ filename = os.path.join(directory, key.replace('/', '_'))
+ try:
+ os.makedirs(directory, exist_ok=True)
+ with open(filename, self.write) as output:
+ output.write(self.encode(value))
+ return True
+ except OSError:
+ log.debug('Failed to store %s to cache:', key, exc_info=True)
+ return False
+
+class FileSystemCache(Cache, FileSystemStorage):
+ def __init__(self, directory, cache_type, *, encode, decode, binary=False):
+ FileSystemStorage.__init__(self, encode, decode, binary)
+ self.base_dir = os.path.join(directory, cache_type)
+
+ def retrieve(self, key):
+ return self._retrieve(self.base_dir, key)
+
+ def store(self, key, value):
+ return self._store(self.base_dir, key, value)
+
+class FileSystemPerJidCache(PerJidCache, FileSystemStorage):
+ def __init__(self, directory, cache_type, *, encode, decode, binary=False):
+ FileSystemStorage.__init__(self, encode, decode, binary)
+ self.base_dir = os.path.join(directory, cache_type)
+
+ def retrieve_by_jid(self, jid, key):
+ directory = os.path.join(self.base_dir, jid.bare)
+ return self._retrieve(directory, key)
+
+ def store_by_jid(self, jid, key, value):
+ directory = os.path.join(self.base_dir, jid.bare)
+ return self._store(directory, key, value)