diff --git a/mockcache.py b/mockcache.py index c2e6ee7..a652daf 100644 --- a/mockcache.py +++ b/mockcache.py @@ -126,20 +126,39 @@ 1 >>> mc.get("a") is None True + >>> mc.set("a b c", 123) #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + MockcachedKeyCharacterError: Control characters not allowed + >>> mc.set(None, 123) #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + 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): + MockcachedStringEncodingError: Key must be str()'s, not unicode. + >>> mc.set("a" * 251, 123) #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + MockcachedKeyLengthError: Key length is > ... """ import datetime -__author__ = "Hong MinHee " +__author__ = "Hong Minhee " __maintainer__ = __author__ -__email__ = "dahlia@lunant.net" -__copyright__ = "Copyright (c) 2010 Lunant " +__email__ = "dahlia@lunant.com" +__copyright__ = "Copyright (c) 2010 Lunant " __license__ = "MIT License" __version__ = "1.0.1" +SERVER_MAX_KEY_LENGTH = 250 +SERVER_MAX_VALUE_LENGTH = 1024*1024 + + class Client(object): """Dictionary-based mock memcached client. Almost like other Python memcached client libraries' interface. @@ -148,6 +167,20 @@ class Client(object): __slots__ = "dictionary", + # exceptions for Client + class MockcachedKeyError(Exception): + pass + class MockcachedKeyLengthError(MockcachedKeyError): + pass + class MockcachedKeyCharacterError(MockcachedKeyError): + pass + class MockcachedKeyNoneError(MockcachedKeyError): + pass + class MockcachedKeyTypeError(MockcachedKeyError): + pass + class MockcachedStringEncodingError(Exception): + pass + def __init__(self, *args, **kwargs): """Does nothing. It takes no or any arguments, but they are just for compatibility so ignored. @@ -233,6 +266,7 @@ def replace(self, key, val, time=0): def set(self, key, val, time=0): """Sets the `key` with `val`.""" + check_key(key) if not time: time = None elif time < 60 * 60 * 24 * 30: @@ -244,6 +278,7 @@ def set(self, key, val, time=0): def get(self, key): """Retrieves a value of the `key` from the internal dictionary.""" + check_key(key) try: val, exptime = self.dictionary[key] except KeyError: @@ -260,7 +295,8 @@ def get_multi(self, keys): """ dictionary = self.dictionary - pairs = ((key, dictionary[key]) for key in keys if key in dictionary) + pairs = ((key, self.dictionary[key]) for key in keys + if key in dictionary) now = datetime.datetime.now return dict((key, value) for key, (value, exp) in pairs if not exp or exp > now()) @@ -269,3 +305,32 @@ def __repr__(self): modname = "" if __name__ == "__main__" else __name__ + "." return "<%sClient %r>" % (modname, self.dictionary) + +def check_key(key, key_extra_len=0): + """Checks sanity of key. Fails if: + Key length is > SERVER_MAX_KEY_LENGTH (Raises MockcachedKeyLengthError). + Contains control characters (Raises MockcachedKeyCharacterError). + Is not a string (Raises MockcachedKeyTypeError) + Is an unicode string (Raises MockcachedStringEncodingError) + Is None (Raises MockcachedKeyNoneError) + """ + import types + if type(key) == types.TupleType: + key = key[1] + if not key: + raise Client.MockcachedKeyNoneError, ("Key is None") + if isinstance(key, unicode): + msg = "Keys must be str()'s, not unicode. Convert your unicode " \ + "strings using mystring.encode(charset)!" + raise Client.MockcachedStringEncodingError, msg + if not isinstance(key, str): + raise Client.MockcachedKeyTypeError, ("Key must be str()'s") + + if isinstance(key, basestring): + if len(key) + key_extra_len > SERVER_MAX_KEY_LENGTH: + raise Client.MockcachedKeyLengthError, ("Key length is > %s" % \ + SERVER_MAX_KEY_LENGTH) + for char in key: + if ord(char) < 33 or ord(char) == 127: + raise Client.MockcachedKeyCharacterError, \ + "Control characters not allowed"