summaryrefslogtreecommitdiff
path: root/requests_cache/backends/memcached.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests_cache/backends/memcached.py')
-rw-r--r--requests_cache/backends/memcached.py83
1 files changed, 83 insertions, 0 deletions
diff --git a/requests_cache/backends/memcached.py b/requests_cache/backends/memcached.py
new file mode 100644
index 0000000..b44aa6d
--- /dev/null
+++ b/requests_cache/backends/memcached.py
@@ -0,0 +1,83 @@
+# TODO: Currently only supports text-based serialization formats.
+# pymemcache does not yet have support for binary or meta protocol.
+# pylibmc does, but requires libmemcached binaries on the host machine,
+# so it's not compatible with memcached running in a Docker container.
+import re
+from telnetlib import Telnet
+
+from pymemcache.client.base import Client
+
+from . import BaseCache, BaseStorage, get_valid_kwargs
+
+KEY_PATTERN = re.compile(rb'ITEM (\S*)')
+
+
+class FileCache(BaseCache):
+ """Memcached backend"""
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self.responses = MemcacheDict('responses', **kwargs)
+ self.redirects = MemcacheDict('redirects', connection=self.responses.connection, **kwargs)
+
+
+class MemcacheDict(BaseStorage):
+ """A dictionary-like interface to memcached"""
+
+ def __init__(
+ self,
+ key_prefix: str = None,
+ connection=None,
+ host='localhost',
+ timeout: float = 5,
+ **kwargs,
+ ):
+ super().__init__(**kwargs)
+ connection_kwargs = get_valid_kwargs(Client, kwargs)
+ self.connection = connection or Client(
+ host,
+ key_prefix=key_prefix,
+ encoding='utf-8',
+ timeout=timeout,
+ **connection_kwargs,
+ )
+
+ def __getitem__(self, key):
+ item = self.connection.get(key)
+ if not item:
+ raise KeyError
+ return item
+
+ def __setitem__(self, key, value):
+ self.connection.set(key, value)
+
+ def __delitem__(self, key):
+ if not self.connection.delete(key):
+ raise KeyError
+
+ # TODO: Is there a cleaner way to do this?
+ def __iter__(self):
+ host, port = self.client.server
+ stats_client = Telnet(host, port)
+ stats_client.write(b'stats cachedump 1 10000\n')
+ output = stats_client.read_until(b'END')
+ return KEY_PATTERN.findall(output)
+
+ def __len__(self):
+ return self.connection.stats().get(b'curr_items', 0)
+
+ def clear(self):
+ self.connection.flush_all()
+
+
+class MemcachePickleDict(MemcacheDict):
+ """Same as :class:`MemcacheDict`, but serializes values before saving"""
+
+ def __setitem__(self, key, value):
+ serialized_value = self.serializer.dumps(value)
+ # if isinstance(serialized_value, bytes):
+ # serialized_value = '?'
+ super().__setitem__(key, serialized_value)
+
+ def __getitem__(self, key):
+ return self.serializer.loads(super().__getitem__(key))