summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McCurdy <andy@andymccurdy.com>2018-11-14 14:53:40 -0800
committerAndy McCurdy <andy@andymccurdy.com>2018-11-14 14:53:40 -0800
commit40025ae073f7b376b496a54491fbb30d964dcb24 (patch)
tree1021807499151f6e53eae56a9eaec642f83037b7
parenta750c7946d41862a38955c35b6928f098911c406 (diff)
downloadredis-py-40025ae073f7b376b496a54491fbb30d964dcb24.tar.gz
only accept bytes, strings, ints, longs and floats as user input
All input sent to Redis is coerced into bytes. This includes key names and values. Prior to this change, redis-py made an effort to cooerce all input into strings by calling str() (Python 3) or unicode() (Python 2). While this works for a handful of types like ints, longs and floats, it fails for other types like bools ('True' or 'False'), None ('None') and many user defined types. Starting with redis-py version 3.0, sending input of any other type is considered an error an a DataError exception will be raised. Fixes #471 Fixes #472 Fixes #321 Fixes #190
-rwxr-xr-xredis/connection.py15
-rw-r--r--tests/test_encoding.py28
2 files changed, 31 insertions, 12 deletions
diff --git a/redis/connection.py b/redis/connection.py
index d473753..3ab9573 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -19,6 +19,7 @@ from redis._compat import (xrange, imap, byte_to_chr, unicode, bytes, long,
LifoQueue, Empty, Full, urlparse, parse_qs,
recv, recv_into, select, unquote)
from redis.exceptions import (
+ DataError,
RedisError,
ConnectionError,
TimeoutError,
@@ -107,13 +108,17 @@ class Encoder(object):
return value.encoded_value
elif isinstance(value, bytes):
return value
- elif isinstance(value, (int, long)):
- value = str(value).encode()
- elif isinstance(value, float):
+ elif isinstance(value, bool):
+ # special case bool since it is a subclass of int
+ raise DataError("Invalid input of type: 'bool'. Convert to a "
+ "byte, string or number first.")
+ elif isinstance(value, (int, long, float)):
value = repr(value).encode()
elif not isinstance(value, basestring):
- # an object we don't know how to deal with. default to unicode()
- value = unicode(value)
+ # a value we don't know how to deal with. throw an error
+ typename = type(value).__name__
+ raise DataError("Invalid input of type: '%s'. Convert to a "
+ "byte, string or number first." % typename)
if isinstance(value, unicode):
value = value.encode(self.encoding, self.encoding_errors)
return value
diff --git a/tests/test_encoding.py b/tests/test_encoding.py
index cff819f..283fc6e 100644
--- a/tests/test_encoding.py
+++ b/tests/test_encoding.py
@@ -24,13 +24,6 @@ class TestEncoding(object):
r.rpush('a', *result)
assert r.lrange('a', 0, -1) == result
- def test_object_value(self, r):
- unicode_string = unichr(3456) + 'abcd' + unichr(3421)
- r['unicode-string'] = Exception(unicode_string)
- cached_val = r['unicode-string']
- assert isinstance(cached_val, unicode)
- assert unicode_string == cached_val
-
class TestCommandsAndTokensArentEncoded(object):
@pytest.fixture()
@@ -39,3 +32,24 @@ class TestCommandsAndTokensArentEncoded(object):
def test_basic_command(self, r):
r.set('hello', 'world')
+
+
+class TestInvalidUserInput(object):
+ def test_boolean_fails(self, r):
+ with pytest.raises(redis.DataError):
+ r.set('a', True)
+
+ def test_none_fails(self, r):
+ with pytest.raises(redis.DataError):
+ r.set('a', None)
+
+ def test_user_type_fails(self, r):
+ class Foo(object):
+ def __str__(self):
+ return 'Foo'
+
+ def __unicode__(self):
+ return 'Foo'
+
+ with pytest.raises(redis.DataError):
+ r.set('a', Foo())