Skip to content

Commit

Permalink
Fix #79, #80 -- Fix storing non-ASCII values on Python 2 and binary v…
Browse files Browse the repository at this point in the history
…alues on Python 3
  • Loading branch information
niconoe authored and timgraham committed Nov 14, 2017
1 parent cc94e72 commit e1f72da
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
* Add flake8 testing and cleanups (PR from Tim Graham, cleanups from Sean
Reifschneider) #112

* Fixed storing non-ASCII values on Python 2 and binary values on Python 3
(PR from Nicolas Noé) #135

Fri, 27 May 2016 13:44:55 -0600 Sean Reifschneider <jafo@tummy.com>

* 1.58 release.
Expand Down
22 changes: 13 additions & 9 deletions memcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class Client(threading.local):
_FLAG_INTEGER = 1 << 1
_FLAG_LONG = 1 << 2
_FLAG_COMPRESSED = 1 << 3
_FLAG_TEXT = 1 << 4

_SERVER_RETRIES = 10 # how many times to try finding a free server.

Expand Down Expand Up @@ -955,11 +956,16 @@ def _val_to_store_info(self, val, min_compress_len):
the new value itself.
"""
flags = 0
if isinstance(val, six.binary_type):
# Check against the exact type, rather than using isinstance(), so that
# subclasses of native types (such as markup-safe strings) are pickled
# and restored as instances of the correct class.
val_type = type(val)
if val_type == six.binary_type:
pass
elif isinstance(val, six.text_type):
elif val_type == six.text_type:
flags |= Client._FLAG_TEXT
val = val.encode('utf-8')
elif isinstance(val, int):
elif val_type == int:
flags |= Client._FLAG_INTEGER
val = '%d' % val
if six.PY3:
Expand Down Expand Up @@ -1250,13 +1256,11 @@ def _recv_value(self, server, flags, rlen):
if flags & Client._FLAG_COMPRESSED:
buf = self.decompressor(buf)
flags &= ~Client._FLAG_COMPRESSED

if flags == 0:
# Bare string
if six.PY3:
val = buf.decode('utf8')
else:
val = buf
# Bare bytes
val = buf
elif flags & Client._FLAG_TEXT:
val = buf.decode('utf-8')
elif flags & Client._FLAG_INTEGER:
val = int(buf)
elif flags & Client._FLAG_LONG:
Expand Down
28 changes: 28 additions & 0 deletions tests/test_memcache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import print_function

import unittest
import zlib

import mock
import six
Expand Down Expand Up @@ -128,6 +130,32 @@ def test_unicode_key(self):
value = self.mc.get(key)
self.assertEqual(value, 5)

def test_unicode_value(self):
key = 'key'
value = u'Iñtërnâtiônàlizætiøn2'
self.mc.set(key, value)
cached_value = self.mc.get(key)
self.assertEqual(value, cached_value)

def test_binary_string(self):
value = 'value_to_be_compressed'
compressed_value = zlib.compress(value.encode())

self.mc.set('binary1', compressed_value)
compressed_result = self.mc.get('binary1')
self.assertEqual(compressed_value, compressed_result)
self.assertEqual(value, zlib.decompress(compressed_result).decode())

self.mc.add('binary1-add', compressed_value)
compressed_result = self.mc.get('binary1-add')
self.assertEqual(compressed_value, compressed_result)
self.assertEqual(value, zlib.decompress(compressed_result).decode())

self.mc.set_multi({'binary1-set_many': compressed_value})
compressed_result = self.mc.get('binary1-set_many')
self.assertEqual(compressed_value, compressed_result)
self.assertEqual(value, zlib.decompress(compressed_result).decode())

def test_ignore_too_large_value(self):
# NOTE: "MemCached: while expecting[...]" is normal...
key = 'keyhere'
Expand Down

0 comments on commit e1f72da

Please sign in to comment.