summaryrefslogtreecommitdiffhomepage
path: root/libs/fcache/cache.py
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fcache/cache.py')
-rw-r--r--libs/fcache/cache.py98
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()