diff options
author | Andy McCurdy <andy@andymccurdy.com> | 2014-12-05 13:03:38 -0500 |
---|---|---|
committer | Andy McCurdy <andy@andymccurdy.com> | 2014-12-05 13:03:38 -0500 |
commit | 38b19cd3098f0c21510a5565dae349bd1caef6dc (patch) | |
tree | e135fe34d53cb9319fd18767f5de72c4dbade30f | |
parent | fa714e08ef59d37b145616cb60939eedafda0bd4 (diff) | |
parent | eddf49d774945986325a04e134b0ba42e0046e70 (diff) | |
download | redis-py-38b19cd3098f0c21510a5565dae349bd1caef6dc.tar.gz |
Merge pull request #564 from hendrik-cliqz/fix-unicode-conversion-in-exception-handling
Fix UnicodeDecodeError in annotate_excpetion
-rw-r--r-- | redis/_compat.py | 11 | ||||
-rwxr-xr-x | redis/client.py | 7 | ||||
-rw-r--r-- | tests/test_scripting.py | 33 |
3 files changed, 48 insertions, 3 deletions
diff --git a/redis/_compat.py b/redis/_compat.py index c7859b5..38d767d 100644 --- a/redis/_compat.py +++ b/redis/_compat.py @@ -12,6 +12,16 @@ if sys.version_info[0] < 3: except ImportError: from StringIO import StringIO as BytesIO + # special unicode handling for python2 to avoid UnicodeDecodeError + def safe_unicode(obj, *args): + """ return the unicode representation of obj """ + try: + return unicode(obj, *args) + except UnicodeDecodeError: + # obj is byte string + ascii_text = str(obj).encode('string_escape') + return unicode(ascii_text) + iteritems = lambda x: x.iteritems() iterkeys = lambda x: x.iterkeys() itervalues = lambda x: x.itervalues() @@ -48,6 +58,7 @@ else: xrange = range basestring = str unicode = str + safe_unicode = str bytes = bytes long = int diff --git a/redis/client.py b/redis/client.py index e444571..70024af 100755 --- a/redis/client.py +++ b/redis/client.py @@ -6,7 +6,8 @@ import warnings import threading import time as mod_time from redis._compat import (b, basestring, bytes, imap, iteritems, iterkeys, - itervalues, izip, long, nativestr, unicode) + itervalues, izip, long, nativestr, unicode, + safe_unicode) from redis.connection import (ConnectionPool, UnixDomainSocketConnection, SSLConnection, Token) from redis.lock import Lock, LuaLock @@ -2532,9 +2533,9 @@ class BasePipeline(object): raise r def annotate_exception(self, exception, number, command): - cmd = unicode(' ').join(imap(unicode, command)) + cmd = safe_unicode(' ').join(imap(safe_unicode, command)) msg = unicode('Command # %d (%s) of pipeline caused error: %s') % ( - number, cmd, unicode(exception.args[0])) + number, cmd, safe_unicode(exception.args[0])) exception.args = (msg,) + exception.args[1:] def parse_response(self, connection, command_name, **options): diff --git a/tests/test_scripting.py b/tests/test_scripting.py index 4849c81..2213ec6 100644 --- a/tests/test_scripting.py +++ b/tests/test_scripting.py @@ -10,6 +10,17 @@ local value = redis.call('GET', KEYS[1]) value = tonumber(value) return value * ARGV[1]""" +msgpack_hello_script = """ +local message = cmsgpack.unpack(ARGV[1]) +local name = message['name'] +return "hello " .. name +""" +msgpack_hello_script_broken = """ +local message = cmsgpack.unpack(ARGV[1]) +local names = message['name'] +return "hello " .. name +""" + class TestScripting(object): @pytest.fixture(autouse=True) @@ -80,3 +91,25 @@ class TestScripting(object): assert r.script_exists(multiply.sha) == [False] # [SET worked, GET 'a', result of multiple script] assert pipe.execute() == [True, b('2'), 6] + + def test_eval_msgpack_pipeline_error_in_lua(self, r): + msgpack_hello = r.register_script(msgpack_hello_script) + assert not msgpack_hello.sha + + pipe = r.pipeline() + + # avoiding a dependency to msgpack, this is the output of + # msgpack.dumps({"name": "joe"}) + msgpack_message_1 = b'\x81\xa4name\xa3Joe' + + msgpack_hello(args=[msgpack_message_1], client=pipe) + + assert r.script_exists(msgpack_hello.sha) == [True] + assert pipe.execute()[0] == b'hello Joe' + + msgpack_hello_broken = r.register_script(msgpack_hello_script_broken) + + msgpack_hello_broken(args=[msgpack_message_1], client=pipe) + with pytest.raises(exceptions.ResponseError) as excinfo: + pipe.execute() + assert excinfo.type == exceptions.ResponseError |