diff --git a/mockcache.py b/mockcache.py
index 0c3d7f3..998f95b 100644
--- a/mockcache.py
+++ b/mockcache.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: ascii -*-
+
# Copyright (c) 2016 Hong Minhee
# Copyright (c) 2010 Lunant
#
@@ -49,88 +52,105 @@
1
>>> mc.get("a")
'1234'
->>> mc
-
+>>> mc.dictionary[b'a']
+('1234', None)
>>> mc.add("a", "1111")
0
>>> mc.get("a")
'1234'
->>> mc
-
+>>> mc.dictionary[b'a']
+('1234', None)
>>> mc.replace("a", "2222")
1
>>> mc.get("a")
'2222'
->>> mc
-
+>>> mc.dictionary[b'a']
+('2222', None)
>>> mc.append("a", "3")
1
>>> mc.get("a")
'22223'
->>> mc
-
+>>> mc.dictionary[b'a']
+('22223', None)
>>> mc.prepend("a", "1")
1
>>> mc.get("a")
'122223'
->>> mc
-
+>>> mc.dictionary[b'a']
+('122223', None)
>>> mc.incr("a")
122224
>>> mc.get("a")
122224
->>> mc
-
+>>> mc.dictionary[b'a']
+(122224, None)
>>> mc.incr("a", 10)
122234
>>> mc.get("a")
122234
->>> mc
-
+>>> mc.dictionary[b'a']
+(122234, None)
>>> mc.decr("a")
122233
>>> mc.get("a")
122233
->>> mc
-
+>>> mc.dictionary[b'a']
+(122233, None)
>>> mc.decr("a", 5)
122228
>>> mc.get("a")
122228
->>> mc
-
+>>> mc.dictionary[b'a']
+(122228, None)
+>>> len(mc.dictionary)
+1
>>> mc.replace("b", "value")
0
>>> mc.get("b")
>>> mc.get("b") is None
True
->>> mc
-
+>>> mc.dictionary[b'a']
+(122228, None)
+>>> len(mc.dictionary)
+1
>>> mc.add("b", "value", 5)
1
>>> mc.get("b")
'value'
->>> mc # doctest: +ELLIPSIS
-
+>>> len(mc.dictionary)
+2
+>>> mc.dictionary[b'a']
+(122228, None)
+>>> mc.dictionary[b'b'] # doctest: +ELLIPSIS
+('value', ...)
>>> import time
>>> time.sleep(6)
>>> mc.get("b")
>>> mc.get("b") is None
True
->>> mc
-
+>>> len(mc.dictionary)
+1
+>>> mc.dictionary[b'a']
+(122228, None)
>>> mc.set("c", "value")
1
->>> mc.get_multi(["a", "b", "c"])
-{'a': 122228, 'c': 'value'}
+>>> multi_result = mc.get_multi(["a", "b", "c"])
+>>> len(multi_result)
+2
+>>> multi_result[b'a']
+122228
+>>> multi_result[b'c']
+'value'
>>> mc.set_multi({"a": 999, "b": 998, "c": 997}, key_prefix="pf_")
[]
>>> mc.get("pf_a")
999
>>> multi_result = mc.get_multi(["b", "c"], key_prefix="pf_")
->>> multi_result["b"]
+>>> len(multi_result)
+2
+>>> multi_result[b"b"]
998
->>> multi_result["c"]
+>>> multi_result[b"c"]
997
>>> mc.delete("a")
1
@@ -144,10 +164,7 @@
MockcachedKeyNoneError: Key is None
>>> mc.set(123, 123) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
-MockcachedKeyTypeError: Key must be str()'s
->>> mc.set(u"a", 123) #doctest: +IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
-MockcachedKeyTypeError: Key must be str()'s, not unicode.
+MockcachedKeyTypeError: Key must be bytes()'s
>>> mc.set("a" * 251, 123) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
MockcachedKeyLengthError: Key length is > ...
@@ -156,19 +173,24 @@
from __future__ import absolute_import
from __future__ import print_function
-from __future__ import unicode_literals
from __future__ import division
+# breaks python 2 tests
+# from __future__ import unicode_literals
import datetime
import copy
+try:
+ unicode
+except NameError:
+ unicode = None.__class__
__author__ = "Hong Minhee "
__maintainer__ = __author__
__email__ = "dahlia@lunant.com"
__copyright__ = "Copyright (c) 2010-2016 Lunant "
__license__ = "MIT License"
-__version__ = "1.0.3_alpha"
+__version__ = "1.1.0"
SERVER_MAX_KEY_LENGTH = 250
@@ -214,6 +236,7 @@ def disconnect_all(self):
def delete(self, key, time=0):
"""Deletes the `key` from the dictionary."""
+ key = check_key(key)
if key in self.dictionary:
if int(time) < 1:
del self.dictionary[key]
@@ -223,6 +246,7 @@ def delete(self, key, time=0):
def incr(self, key, delta=1):
"""Increments an integer by the `key`."""
+ key = check_key(key)
try:
value, exp = self.dictionary[key]
except KeyError:
@@ -234,6 +258,7 @@ def incr(self, key, delta=1):
def decr(self, key, delta=1):
"""Decrements an integer by the `key`."""
+ key = check_key(key)
return self.incr(key, -delta)
def append(self, key, val):
@@ -241,6 +266,7 @@ def append(self, key, val):
It works only when there is the key already.
"""
+ key = check_key(key)
try:
self.dictionary[key] = str(self.dictionary[key][0]) + val, \
self.dictionary[key][1]
@@ -254,6 +280,7 @@ def prepend(self, key, val):
It works only when there is the key already.
"""
+ key = check_key(key)
try:
self.dictionary[key] = val + str(self.dictionary[key][0]), \
self.dictionary[key][1]
@@ -267,6 +294,7 @@ def add(self, key, val, time=0):
but it stores the value only when the `key` doesn't exist already.
"""
+ key = check_key(key)
if key in self.dictionary:
return 0
return self.set(key, val, time)
@@ -276,13 +304,14 @@ def replace(self, key, val, time=0):
but it store the value only when the `key` already exists.
"""
+ key = check_key(key)
if key not in self.dictionary:
return 0
return self.set(key, val, time)
def set(self, key, val, time=0):
"""Sets the `key` with `val`."""
- check_key(key)
+ key = check_key(key)
if not time:
time = None
elif time < 60 * 60 * 24 * 30:
@@ -295,13 +324,19 @@ def set(self, key, val, time=0):
def set_multi(self, mapping, time=0, key_prefix=b''):
"""Sets all the key-value pairs in `mapping`. If `key_prefix` is
given, it is prepended to all keys in `mapping`."""
+ if not isinstance(key_prefix, bytes):
+ try:
+ key_prefix = key_prefix.encode('ascii')
+ except Exception:
+ key_prefix = b''
for key, value in mapping.items():
- self.set(b'{0}{1}'.format(key_prefix, key), value, time)
+ key = check_key(key)
+ self.set(b''.join((key_prefix, key)), value, time)
return []
def get(self, key):
"""Retrieves a value of the `key` from the internal dictionary."""
- check_key(key)
+ key = check_key(key)
try:
val, exptime = self.dictionary[key]
except KeyError:
@@ -317,13 +352,19 @@ def get_multi(self, keys, key_prefix=b''):
dictionary. If `key_prefix` is given, it is prepended to all
keys before retrieving them.
"""
+ if not isinstance(key_prefix, bytes):
+ try:
+ key_prefix = key_prefix.encode('ascii')
+ except Exception:
+ key_prefix = b''
dictionary = self.dictionary
- prefixed_keys = [(key, b'{0}{1}'.format(key_prefix, key))
- for key in keys]
- pairs = ((key, self.dictionary[prefixed])
- for (key, prefixed) in prefixed_keys
- if prefixed in dictionary)
+ pairs = []
+ for key in keys:
+ key = check_key(key)
+ prefixed_key = b''.join((key_prefix, key))
+ if prefixed_key in dictionary:
+ pairs.append((key, self.dictionary[prefixed_key]))
now = datetime.datetime.now
return dict((key, copy.deepcopy(value)) for key, (value, exp) in pairs
if not exp or exp > now())
@@ -334,6 +375,7 @@ def delete_multi(self, keys):
"""
result = True
for key in keys:
+ key = check_key(key)
result = result and self.delete(key)
return result
@@ -349,6 +391,10 @@ def __len__(self):
return len(self.dictionary)
def __contains__(self, key):
+ try:
+ key = check_key(key)
+ except MockcachedKeyError:
+ pass
return key in self.dictionary
@@ -362,14 +408,25 @@ def check_key(key, key_extra_len=0):
if type(key) == tuple:
key = key[1]
if not key:
- raise Client.MockcachedKeyNoneError("Key is None")
- if not isinstance(key, str):
- raise Client.MockcachedKeyTypeError("Key must be str()'s")
+ raise Client.MockcachedKeyNoneError('Key is None')
+
+ if isinstance(key, unicode):
+ raise Client.MockcachedKeyTypeError(
+ "Key must be bytes()'s, not unicode")
+ if not isinstance(key, bytes):
+ try:
+ key = key.encode('ascii')
+ except Exception:
+ raise Client.MockcachedKeyTypeError(
+ 'Key must be encodable to ascii')
if len(key) + key_extra_len > SERVER_MAX_KEY_LENGTH:
- raise Client.MockcachedKeyLengthError("Key length is > %s" % \
+ raise Client.MockcachedKeyLengthError("Key length is > %s" % \
SERVER_MAX_KEY_LENGTH)
for char in key:
- if ord(char) < 33 or ord(char) == 127:
+ if isinstance(char, str):
+ char = ord(char)
+ if char < 33 or char == 127:
raise Client.MockcachedKeyCharacterError("Control characters not "
"allowed")
+ return key