diff options
Diffstat (limited to 'libs/fcache/cache.py')
-rw-r--r-- | libs/fcache/cache.py | 98 |
1 files changed, 52 insertions, 46 deletions
diff --git a/libs/fcache/cache.py b/libs/fcache/cache.py index 32a8a769b..7775cb21f 100644 --- a/libs/fcache/cache.py +++ b/libs/fcache/cache.py @@ -1,4 +1,5 @@ import codecs +from collections.abc import MutableMapping import logging import os import pickle @@ -7,14 +8,6 @@ import tempfile import appdirs -try: - from collections.abc import MutableMapping - unicode = str -except ImportError: - # Python 2 imports - from collections import MutableMapping - FileNotFoundError = IOError - from .posixemulation import rename logger = logging.getLogger(__name__) @@ -33,14 +26,14 @@ class FileCache(MutableMapping): .. NOTE:: Keys and values are always stored as :class:`bytes` objects. If data - serialization is enabled, keys are returned as :class:`str` or - :class:`unicode` objects. + serialization is enabled, keys are returned as :class:`str` objects. If data serialization is disabled, keys are returned as a :class:`bytes` object. :param str appname: The app/script the cache should be associated with. :param str flag: How the cache should be opened. See below for details. - :param mode: The Unix mode for the cache files or False to prevent changing permissions. + :param mode: The Unix mode for the cache files or False to prevent changing + permissions. :param str keyencoding: The encoding the keys use, defaults to 'utf-8'. This is used if *serialize* is ``False``; the keys are treated as :class:`bytes` objects. @@ -85,57 +78,66 @@ class FileCache(MutableMapping): """ - def __init__(self, appname, flag='c', mode=0o666, keyencoding='utf-8', - serialize=True, app_cache_dir=None): + def __init__( + self, + appname, + flag="c", + mode=0o666, + keyencoding="utf-8", + serialize=True, + app_cache_dir=None, + ): """Initialize a :class:`FileCache` object.""" if not isinstance(flag, str): raise TypeError("flag must be str not '{}'".format(type(flag))) - elif flag[0] not in 'rwcn': - raise ValueError("invalid flag: '{}', first flag must be one of " - "'r', 'w', 'c' or 'n'".format(flag)) - elif len(flag) > 1 and flag[1] != 's': - raise ValueError("invalid flag: '{}', second flag must be " - "'s'".format(flag)) + elif flag[0] not in "rwcn": + raise ValueError( + "invalid flag: '{}', first flag must be one of " + "'r', 'w', 'c' or 'n'".format(flag) + ) + elif len(flag) > 1 and flag[1] != "s": + raise ValueError( + "invalid flag: '{}', second flag must be " "'s'".format(flag) + ) appname, subcache = self._parse_appname(appname) - if 'cache' in subcache: + if "cache" in subcache: raise ValueError("invalid subcache name: 'cache'.") self._is_subcache = bool(subcache) if not app_cache_dir: app_cache_dir = appdirs.user_cache_dir(appname, appname) subcache_dir = os.path.join(app_cache_dir, *subcache) - self.cache_dir = os.path.join(subcache_dir, 'cache') + self.cache_dir = os.path.join(subcache_dir, "cache") exists = os.path.exists(self.cache_dir) - if len(flag) > 1 and flag[1] == 's': + if len(flag) > 1 and flag[1] == "s": self._sync = True else: self._sync = False self._buffer = {} - if exists and 'n' in flag: + if exists and "n" in flag: self.clear() self.create() - elif not exists and ('c' in flag or 'n' in flag): + elif not exists and ("c" in flag or "n" in flag): self.create() elif not exists: - raise FileNotFoundError("no such directory: '{}'".format( - self.cache_dir)) + raise FileNotFoundError("no such directory: '{}'".format(self.cache_dir)) - self._flag = 'rb' if 'r' in flag else 'wb' + self._flag = "rb" if "r" in flag else "wb" self._mode = mode self._keyencoding = keyencoding self._serialize = serialize def _parse_appname(self, appname): """Splits an appname into the appname and subcache components.""" - components = appname.split('.') + components = appname.split(".") return components[0], components[1:] def create(self): """Create the write buffer and cache directory.""" - if not self._sync and not hasattr(self, '_buffer'): + if not self._sync and not hasattr(self, "_buffer"): self._buffer = {} if not os.path.exists(self.cache_dir): os.makedirs(self.cache_dir) @@ -195,11 +197,11 @@ class FileCache(MutableMapping): :class:`str`. """ - if isinstance(key, str) or isinstance(key, unicode): + if isinstance(key, str): key = key.encode(self._keyencoding) elif not isinstance(key, bytes): raise TypeError("key must be bytes or str") - return codecs.encode(key, 'hex_codec').decode(self._keyencoding) + return codecs.encode(key, "hex_codec").decode(self._keyencoding) def _decode_key(self, key): """Decode key using hex_codec to retrieve the original key. @@ -208,7 +210,7 @@ class FileCache(MutableMapping): Keys are returned as :class:`bytes` if serialization is disabled. """ - bkey = codecs.decode(key.encode(self._keyencoding), 'hex_codec') + bkey = codecs.decode(key.encode(self._keyencoding), "hex_codec") return bkey.decode(self._keyencoding) if self._serialize else bkey def _dumps(self, value): @@ -228,8 +230,10 @@ class FileCache(MutableMapping): def _all_filenames(self): """Return a list of absolute cache filenames""" try: - return [os.path.join(self.cache_dir, filename) for filename in - os.listdir(self.cache_dir)] + return [ + os.path.join(self.cache_dir, filename) + for filename in os.listdir(self.cache_dir) + ] except (FileNotFoundError, OSError): return [] @@ -252,12 +256,8 @@ class FileCache(MutableMapping): def _read_from_file(self, filename): """Read data from filename.""" - try: - with open(filename, 'rb') as f: - return self._loads(f.read()) - except (IOError, OSError): - logger.warning('Error opening file: {}'.format(filename)) - return None + with open(filename, "rb") as f: + return self._loads(f.read()) def __setitem__(self, key, value): ekey = self._encode_key(key) @@ -281,17 +281,17 @@ class FileCache(MutableMapping): def __delitem__(self, key): ekey = self._encode_key(key) - filename = self._key_to_filename(ekey) + found_in_buffer = hasattr(self, "_buffer") and ekey in self._buffer if not self._sync: try: del self._buffer[ekey] except KeyError: - if filename not in self._all_filenames(): - raise KeyError(key) - try: + pass + filename = self._key_to_filename(ekey) + if filename in self._all_filenames(): os.remove(filename) - except (IOError, OSError): - pass + elif not found_in_buffer: + raise KeyError(key) def __iter__(self): for key in self._all_keys(): @@ -303,3 +303,9 @@ class FileCache(MutableMapping): def __contains__(self, key): ekey = self._encode_key(key) return ekey in self._all_keys() + + def __enter__(self): + return self + + def __exit__(self, type_, value, traceback): + self.close() |