1'''
2Provides several CacheStore backends for Cheetah's caching framework.  The
3methods provided by these classes have the same semantics as those in the
4python-memcached API, except for their return values:
5
6set(key, val, time=0)
7  set the value unconditionally
8add(key, val, time=0)
9  set only if the server doesn't already have this key
10replace(key, val, time=0)
11  set only if the server already have this key
12get(key, val)
13  returns val or raises a KeyError
14delete(key)
15  deletes or raises a KeyError
16'''
17import time
18
19class Error(Exception):
20    pass
21
22class AbstractCacheStore(object):
23
24    def set(self, key, val, time=None):
25        raise NotImplementedError
26
27    def add(self, key, val, time=None):
28        raise NotImplementedError
29
30    def replace(self, key, val, time=None):
31        raise NotImplementedError
32
33    def delete(self, key):
34        raise NotImplementedError
35
36    def get(self, key):
37        raise NotImplementedError
38
39class MemoryCacheStore(AbstractCacheStore):
40    def __init__(self):
41        self._data = {}
42
43    def set(self, key, val, time=0):
44        self._data[key] = (val, time)
45
46    def add(self, key, val, time=0):
47        if key in self._data:
48            raise Error('a value for key %r is already in the cache'%key)
49        self._data[key] = (val, time)
50
51    def replace(self, key, val, time=0):
52        if key in self._data:
53            raise Error('a value for key %r is already in the cache'%key)
54        self._data[key] = (val, time)
55
56    def delete(self, key):
57        del self._data[key]
58
59    def get(self, key):
60        (val, exptime) = self._data[key]
61        if exptime and time.time() > exptime:
62            del self._data[key]
63            raise KeyError(key)
64        else:
65            return val
66
67    def clear(self):
68        self._data.clear()
69
70class MemcachedCacheStore(AbstractCacheStore):
71    servers = ('127.0.0.1:11211')
72    def __init__(self, servers=None, debug=False):
73        if servers is None:
74            servers = self.servers
75        from memcache import Client as MemcachedClient
76        self._client = MemcachedClient(servers, debug)
77
78    def set(self, key, val, time=0):
79        self._client.set(key, val, time)
80
81    def add(self, key, val, time=0):
82        res = self._client.add(key, val, time)
83        if not res:
84            raise Error('a value for key %r is already in the cache'%key)
85        self._data[key] = (val, time)
86
87    def replace(self, key, val, time=0):
88        res = self._client.replace(key, val, time)
89        if not res:
90            raise Error('a value for key %r is already in the cache'%key)
91        self._data[key] = (val, time)
92
93    def delete(self, key):
94        res = self._client.delete(key, time=0)
95        if not res:
96            raise KeyError(key)
97
98    def get(self, key):
99        val = self._client.get(key)
100        if val is None:
101            raise KeyError(key)
102        else:
103            return val
104
105    def clear(self):
106        self._client.flush_all()
107