blob: b28de9ac62d84533909c0902315ade453326f1f3 [file] [edit]
# 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 typing import Optional
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: Optional[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))