diff -Nru python-memcache-1.57/ChangeLog python-memcache-1.59/ChangeLog --- python-memcache-1.57/ChangeLog 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/ChangeLog 2017-12-15 04:15:52.000000000 +0000 @@ -1,3 +1,46 @@ + * Added testing for Python 3.5 and 3.6 (PR from Tim Graham) #110, #131 + + * Fixed typos in docstrings (PR from Romuald Brunet, reviewed by Tim + Graham) #105 + + * Removing Python 2.6, 3.2, and 3.3 testing (PR from Tim Graham) #115, #116 + + * Removing unnecessary parens in return statements (PR from Tim Graham) + #113 + + * Remove unused _has_unicode/_str_cls vars (PR from Tim Graham) #111 + + * 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 + + * Fixed touch(..., time=0) command (PR from Nicolas Noé) #137 + +Fri, 27 May 2016 13:44:55 -0600 Sean Reifschneider + + * 1.58 release. + + * Fixing a performance regression in v1.54 in Python 2, using cPickle again. + Patch by edmorley #86 + + * Support for "stats slabs". + Patch by grg350 #93 + + * get_stats encoding fix. + Patch by bartTC #91 + + * Pin Six version to >= 1.4 + Patch by pipermirriam #81 + + * setup.py build process pulls version from memcached.py + Patch by dieselmachine #72 + + * delete() and delete_multi() now default the "time" argument to None, + since the protocol doesn't allow a time in some implementations. + Patch by oremj #27 + Fri, 31 Jul 2015 11:38:25 -0600 Sean Reifschneider * 1.57 release. diff -Nru python-memcache-1.57/debian/changelog python-memcache-1.59/debian/changelog --- python-memcache-1.57/debian/changelog 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/changelog 2019-03-14 07:23:27.000000000 +0000 @@ -1,14 +1,38 @@ -python-memcache (1.57-2) unstable; urgency=medium +python-memcache (1.59-1~cloud0) bionic-stein; urgency=medium + * New update for the Ubuntu Cloud Archive. + + -- Openstack Ubuntu Testing Bot Thu, 14 Mar 2019 07:23:27 +0000 + +python-memcache (1.59-1) unstable; urgency=medium + + [ Ondřej Nový ] * Standards-Version is 3.9.7 now (no change). * Added Debian tests. * Standards-Version is 3.9.8 now (no change) * d/rules: Changed UPSTREAM_GIT protocol to https * d/copyright: Changed source URL to https protocol - * d/s/options: extend-diff-ignore of .gitreview - * d/control: Using OpenStack's Gerrit as VCS URLs. + * d/control: Use team+openstack@tracker.debian.org as maintainer + + [ Daniel Baumann ] + * Updating copyright format url. + * Updating maintainer field. + * Running wrap-and-sort -bast. + * Removing gbp.conf, not used anymore or should be specified in the + developers dotfiles. + * Updating standards version to 4.1.0. + + [ Ondřej Nový ] + * d/control: Set Vcs-* to salsa.debian.org + * d/changelog: Remove trailing whitespaces + + [ Thomas Goirand ] + * New upstream release: + - Fix Storing non-ascii values in memcache fails on Py 2 (Closes: #894773). + * Removed More_Python_3_fixes.patch, kind of applied upstream. + * Fixed (build-)depends for this release. - -- Ondřej Nový Sat, 27 Feb 2016 18:44:10 +0100 + -- Thomas Goirand Thu, 06 Sep 2018 01:18:05 +0200 python-memcache (1.57-1) unstable; urgency=medium @@ -122,7 +146,7 @@ - bump Standards-Version to 3.8.3 - bump required python version. * debian/watch - - added the pasv option. + - added the pasv option. * debian/copyright - Modified (C) to ©. * debian rules diff -Nru python-memcache-1.57/debian/control python-memcache-1.59/debian/control --- python-memcache-1.57/debian/control 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/control 2018-09-05 23:18:05.000000000 +0000 @@ -1,31 +1,38 @@ Source: python-memcache Section: python Priority: optional -Maintainer: PKG OpenStack -Uploaders: Carl Chenet , - Thomas Goirand , -Build-Depends: debhelper (>= 9), - dh-python, - python-all, - python-setuptools, - python3-all, - python3-setuptools, -Build-Depends-Indep: memcached, - python-nose, - python-six, - python3-nose, - python3-six, -Standards-Version: 3.9.8 -Vcs-Git: https://git.openstack.org/openstack/deb-python-memcache -b debian/newton -Vcs-Browser: https://git.openstack.org/cgit/openstack/deb-python-memcache?h=debian%2Fnewton +Maintainer: Debian OpenStack +Uploaders: + Carl Chenet , + Thomas Goirand , +Build-Depends: + debhelper (>= 9), + dh-python, + python-all, + python-setuptools, + python3-all, + python3-setuptools, +Build-Depends-Indep: + memcached, + python-mock, + python-nose, + python-six, + python3-mock, + python3-nose, + python3-six, +Standards-Version: 4.1.0 +Vcs-Git: https://salsa.debian.org/openstack-team/python/python-memcache.git +Vcs-Browser: https://salsa.debian.org/openstack-team/python/python-memcache Homepage: https://github.com/linsomniac/python-memcached Package: python-memcache Architecture: all -Depends: python-six, - ${misc:Depends}, - ${python:Depends}, -Suggests: memcached, +Depends: + python-six, + ${misc:Depends}, + ${python:Depends}, +Suggests: + memcached, Description: pure Python memcached client This software is a 100% Python interface to the memcached memory cache daemon. It is the client side software which allows storing values in @@ -35,10 +42,12 @@ Package: python3-memcache Architecture: all -Depends: python3-six, - ${misc:Depends}, - ${python3:Depends}, -Suggests: memcached, +Depends: + python3-six, + ${misc:Depends}, + ${python3:Depends}, +Suggests: + memcached, Description: pure Python memcached client - Python 3.x This software is a 100% Python interface to the memcached memory cache daemon. It is the client side software which allows storing values in one or more, diff -Nru python-memcache-1.57/debian/copyright python-memcache-1.59/debian/copyright --- python-memcache-1.57/debian/copyright 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/copyright 2018-09-05 23:18:05.000000000 +0000 @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: python-memcached Upstream-Contact: Sean Reifschneider Source: https://github.com/linsomniac/python-memcached diff -Nru python-memcache-1.57/debian/gbp.conf python-memcache-1.59/debian/gbp.conf --- python-memcache-1.57/debian/gbp.conf 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -[DEFAULT] -upstream-branch = master -debian-branch = debian/newton -upstream-tag = %(version)s -compression = xz - -[buildpackage] -export-dir = ../build-area/ - diff -Nru python-memcache-1.57/debian/patches/More_Python_3_fixes.patch python-memcache-1.59/debian/patches/More_Python_3_fixes.patch --- python-memcache-1.57/debian/patches/More_Python_3_fixes.patch 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/patches/More_Python_3_fixes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,579 +0,0 @@ -Description: [PATCH] More Python 3 fixes - * Port set_multi() to Python 3 - * Port delete_multi() to Python 3 - * Fix _get_server() on Python 3 when the connection to the first server - fails: encode to ASCII before calling serverHashFunction. - * Fix expect(): don't decode line on Python 3, return the raw line - * Add more unit tests - * tox now also runs unit tests - * Explicit the encoding when calling str.encode(): use 'utf-8' - * test_memcache: close sockets in tearDown() - * test_get_unknown_value(): delete the key to ensure that it doesn't exist -Author: Victor Stinner -Date: Mon, 27 Jul 2015 18:08:10 +0200 -Origin: https://github.com/haypo/python-memcached/commit/3fde65eeae0019ec0971442886331fb7ffbcdffb.patch -Last-Update: 2016-02-05 - ---- python-memcache-1.57.orig/memcache.py -+++ python-memcache-1.57/memcache.py -@@ -258,12 +258,12 @@ class Client(threading.local): - return key - - def _encode_cmd(self, cmd, key, headers, noreply, *args): -- cmd_bytes = cmd.encode() if six.PY3 else cmd -+ cmd_bytes = cmd.encode('utf-8') if six.PY3 else cmd - fullcmd = [cmd_bytes, b' ', key] - - if headers: - if six.PY3: -- headers = headers.encode() -+ headers = headers.encode('utf-8') - fullcmd.append(b' ') - fullcmd.append(headers) - -@@ -410,7 +410,10 @@ class Client(threading.local): - if server.connect(): - # print("(using server %s)" % server,) - return server, key -- serverhash = serverHashFunction(str(serverhash) + str(i)) -+ serverhash = str(serverhash) + str(i) -+ if isinstance(serverhash, six.text_type): -+ serverhash = serverhash.encode('ascii') -+ serverhash = serverHashFunction(serverhash) - return None, None - - def disconnect_all(self): -@@ -456,15 +459,15 @@ class Client(threading.local): - for server in six.iterkeys(server_keys): - bigcmd = [] - write = bigcmd.append -- extra = ' noreply' if noreply else '' - if time is not None: -- for key in server_keys[server]: # These are mangled keys -- write("delete %s %d%s\r\n" % (key, time, extra)) -+ headers = str(time) - else: -- for key in server_keys[server]: # These are mangled keys -- write("delete %s%s\r\n" % (key, extra)) -+ headers = None -+ for key in server_keys[server]: # These are mangled keys -+ cmd = self._encode_cmd('delete', key, headers, noreply, b'\r\n') -+ write(cmd) - try: -- server.send_cmds(''.join(bigcmd)) -+ server.send_cmds(b''.join(bigcmd)) - except socket.error as msg: - rc = 0 - if isinstance(msg, tuple): -@@ -483,7 +486,7 @@ class Client(threading.local): - for server, keys in six.iteritems(server_keys): - try: - for key in keys: -- server.expect("DELETED") -+ server.expect(b"DELETED") - except socket.error as msg: - if isinstance(msg, tuple): - msg = msg[1] -@@ -528,9 +531,10 @@ class Client(threading.local): - return 0 - self._statlog(cmd) - if time is not None and time != 0: -- fullcmd = self._encode_cmd(cmd, key, str(time), noreply) -+ headers = str(time) - else: -- fullcmd = self._encode_cmd(cmd, key, None, noreply) -+ headers = None -+ fullcmd = self._encode_cmd(cmd, key, headers, noreply) - - try: - server.send_cmd(fullcmd) -@@ -905,12 +909,12 @@ class Client(threading.local): - - # short-circuit if there are no servers, just return all keys - if not server_keys: -- return(mapping.keys()) -+ return list(mapping.keys()) - - for server, keys in six.iteritems(server_keys): - try: - for key in keys: -- if server.readline() == 'STORED': -+ if server.readline() == b'STORED': - continue - else: - # un-mangle. -@@ -1039,7 +1043,7 @@ class Client(threading.local): - self._statlog(cmd) - - try: -- cmd_bytes = cmd.encode() if six.PY3 else cmd -+ cmd_bytes = cmd.encode('utf-8') if six.PY3 else cmd - fullcmd = b''.join((cmd_bytes, b' ', key)) - server.send_cmd(fullcmd) - rkey = flags = rlen = cas_id = None -@@ -1434,9 +1438,11 @@ class _Host(object): - if self.debug and line != text: - if six.PY3: - text = text.decode('utf8') -- line = line.decode('utf8', 'replace') -+ log_line = line.decode('utf8', 'replace') -+ else: -+ log_line = line - self.debuglog("while expecting %r, got unexpected response %r" -- % (text, line)) -+ % (text, log_line)) - return line - - def recv(self, rlen): -@@ -1472,236 +1478,13 @@ def _doctest(): - import doctest - import memcache - servers = ["127.0.0.1:11211"] -- mc = Client(servers, debug=1) -+ mc = memcache.Client(servers, debug=1) - globs = {"mc": mc} -- return doctest.testmod(memcache, globs=globs) -- --if __name__ == "__main__": -- failures = 0 -- print("Testing docstrings...") -- _doctest() -- print("Running tests:") -- print() -- serverList = [["127.0.0.1:11211"]] -- if '--do-unix' in sys.argv: -- serverList.append([os.path.join(os.getcwd(), 'memcached.socket')]) -- -- for servers in serverList: -- mc = Client(servers, debug=1) -- -- def to_s(val): -- if not isinstance(val, _str_cls): -- return "%s (%s)" % (val, type(val)) -- return "%s" % val -- -- def test_setget(key, val, noreply=False): -- global failures -- print("Testing set/get (noreply=%s) {'%s': %s} ..." -- % (noreply, to_s(key), to_s(val)), end=" ") -- mc.set(key, val, noreply=noreply) -- newval = mc.get(key) -- if newval == val: -- print("OK") -- return 1 -- else: -- print("FAIL") -- failures += 1 -- return 0 -- -- class FooStruct(object): -- -- def __init__(self): -- self.bar = "baz" -- -- def __str__(self): -- return "A FooStruct" -- -- def __eq__(self, other): -- if isinstance(other, FooStruct): -- return self.bar == other.bar -- return 0 -- -- test_setget("a_string", "some random string") -- test_setget("a_string_2", "some random string", noreply=True) -- test_setget("an_integer", 42) -- test_setget("an_integer_2", 42, noreply=True) -- if six.PY3: -- ok = test_setget("long", 1 << 30) -- else: -- ok = test_setget("long", long(1 << 30)) -- if ok: -- print("Testing delete ...", end=" ") -- if mc.delete("long"): -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- print("Checking results of delete ...", end=" ") -- if mc.get("long") is None: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- print("Testing get_multi ...",) -- print(mc.get_multi(["a_string", "an_integer", "a_string_2", -- "an_integer_2"])) -- -- # removed from the protocol -- # if test_setget("timed_delete", 'foo'): -- # print "Testing timed delete ...", -- # if mc.delete("timed_delete", 1): -- # print("OK") -- # else: -- # print("FAIL") -- # failures += 1 -- # print "Checking results of timed delete ..." -- # if mc.get("timed_delete") is None: -- # print("OK") -- # else: -- # print("FAIL") -- # failures += 1 -- -- print("Testing get(unknown value) ...", end=" ") -- print(to_s(mc.get("unknown_value"))) -- -- f = FooStruct() -- test_setget("foostruct", f) -- test_setget("foostruct_2", f, noreply=True) -- -- print("Testing incr ...", end=" ") -- x = mc.incr("an_integer", 1) -- if x == 43: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing incr (noreply=True) ...", end=" ") -- mc.incr("an_integer_2", 1, noreply=True) -- x = mc.get("an_integer_2") -- if x == 43: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing decr ...", end=" ") -- x = mc.decr("an_integer", 1) -- if x == 42: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- sys.stdout.flush() -- -- print("Testing decr (noreply=True) ...", end=" ") -- mc.decr("an_integer_2", 1, noreply=True) -- x = mc.get("an_integer_2") -- if x == 42: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- sys.stdout.flush() -- -- # sanity tests -- print("Testing sending spaces...", end=" ") -- sys.stdout.flush() -- try: -- x = mc.set("this has spaces", 1) -- except Client.MemcachedKeyCharacterError as msg: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing sending control characters...", end=" ") -- try: -- x = mc.set("this\x10has\x11control characters\x02", 1) -- except Client.MemcachedKeyCharacterError as msg: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing using insanely long key...", end=" ") -- try: -- x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1) -- x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1, noreply=True) -- except Client.MemcachedKeyLengthError as msg: -- print("FAIL") -- failures += 1 -- else: -- print("OK") -- try: -- x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'a', 1) -- except Client.MemcachedKeyLengthError as msg: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing sending a unicode-string key...", end=" ") -- try: -- x = mc.set(unicode('keyhere'), 1) -- except Client.MemcachedStringEncodingError as msg: -- print("OK", end=" ") -- else: -- print("FAIL", end=" ") -- failures += 1 -- try: -- x = mc.set((unicode('a')*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) -- except Client.MemcachedKeyError: -- print("FAIL", end=" ") -- failures += 1 -- else: -- print("OK", end=" ") -- s = pickle.loads('V\\u4f1a\np0\n.') -- try: -- x = mc.set((s * SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) -- except Client.MemcachedKeyLengthError: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing using a value larger than the memcached value limit...") -- print('NOTE: "MemCached: while expecting[...]" is normal...') -- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH) -- if mc.get('keyhere') is None: -- print("OK", end=" ") -- else: -- print("FAIL", end=" ") -- failures += 1 -- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa') -- if mc.get('keyhere') is None: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing set_multi() with no memcacheds running", end=" ") -- mc.disconnect_all() -- errors = mc.set_multi({'keyhere': 'a', 'keythere': 'b'}) -- if errors != []: -- print("FAIL") -- failures += 1 -- else: -- print("OK") -- -- print("Testing delete_multi() with no memcacheds running", end=" ") -- mc.disconnect_all() -- ret = mc.delete_multi({'keyhere': 'a', 'keythere': 'b'}) -- if ret != 1: -- print("FAIL") -- failures += 1 -- else: -- print("OK") -- -- if failures > 0: -- print('*** THERE WERE FAILED TESTS') -+ results = doctest.testmod(memcache, globs=globs) -+ mc.disconnect_all() -+ print("Doctests: %s" % (results,)) -+ if results.failed: - sys.exit(1) -- sys.exit(0) - - - # vim: ts=4 sw=4 et : ---- python-memcache-1.57.orig/tests/test_memcache.py -+++ python-memcache-1.57/tests/test_memcache.py -@@ -1,10 +1,11 @@ - from __future__ import print_function - --from unittest import TestCase -+import time -+import unittest - - import six - --from memcache import Client, SERVER_MAX_KEY_LENGTH -+from memcache import Client, SERVER_MAX_KEY_LENGTH, SERVER_MAX_VALUE_LENGTH - - try: - _str_cls = basestring -@@ -32,12 +33,14 @@ class FooStruct(object): - return 0 - - --class TestMemcache(TestCase): -+class TestMemcache(unittest.TestCase): - def setUp(self): - # TODO: unix socket server stuff - servers = ["127.0.0.1:11212"] - self.mc = Client(servers, debug=1) -- pass -+ -+ def tearDown(self): -+ self.mc.disconnect_all() - - def check_setget(self, key, val, noreply=False): - self.mc.set(key, val, noreply=noreply) -@@ -64,6 +67,8 @@ class TestMemcache(TestCase): - {"gm_an_integer": 42, "gm_a_string": "some random string"}) - - def test_get_unknown_value(self): -+ self.mc.delete("unknown_value") -+ - self.assertEqual(self.mc.get("unknown_value"), None) - - def test_setget_foostruct(self): -@@ -120,79 +125,55 @@ class TestMemcache(TestCase): - self.mc.set('a' * SERVER_MAX_KEY_LENGTH, 1) - self.mc.set('a' * SERVER_MAX_KEY_LENGTH, 1, noreply=True) - -+ def test_unicode_key(self): -+ s = six.u('\u4f1a') -+ maxlen = SERVER_MAX_KEY_LENGTH // len(s.encode('utf-8')) -+ key = s * maxlen -+ -+ self.mc.set(key, 5) -+ value = self.mc.get(key) -+ self.assertEqual(value, 5) -+ -+ def test_ignore_too_large_value(self): -+ # NOTE: "MemCached: while expecting[...]" is normal... -+ key = 'keyhere' -+ -+ value = 'a' * (SERVER_MAX_VALUE_LENGTH // 2) -+ self.assertTrue(self.mc.set(key, value)) -+ self.assertEqual(self.mc.get(key), value) -+ -+ value = 'a' * SERVER_MAX_VALUE_LENGTH -+ self.assertFalse(self.mc.set(key, value)) -+ # This test fails if the -I option is used on the memcached server -+ self.assertIsNone(self.mc.get(key)) -+ -+ def test_get_set_multi_key_prefix(self): -+ """Testing set_multi() with no memcacheds running.""" -+ -+ prefix = 'pfx_' -+ values = {'key1': 'a', 'key2': 'b'} -+ errors = self.mc.set_multi(values, key_prefix=prefix) -+ self.assertEqual(errors, []) -+ -+ keys = list(values) -+ self.assertEqual(self.mc.get_multi(keys, key_prefix=prefix), -+ values) -+ -+ def test_set_multi_dead_servers(self): -+ """Testing set_multi() with no memcacheds running.""" -+ -+ self.mc.disconnect_all() -+ for server in self.mc.servers: -+ server.mark_dead('test') -+ errors = self.mc.set_multi({'key1': 'a', 'key2': 'b'}) -+ self.assertEqual(sorted(errors), ['key1', 'key2']) -+ -+ def test_disconnect_all_delete_multi(self): -+ """Testing delete_multi() with no memcacheds running.""" -+ self.mc.disconnect_all() -+ ret = self.mc.delete_multi({'keyhere': 'a', 'keythere': 'b'}) -+ self.assertEqual(ret, 1) - --if __name__ == "__main__": -- # failures = 0 -- # print("Testing docstrings...") -- # _doctest() -- # print("Running tests:") -- # print() -- # serverList = [["127.0.0.1:11211"]] -- # if '--do-unix' in sys.argv: -- # serverList.append([os.path.join(os.getcwd(), 'memcached.socket')]) -- -- # for servers in serverList: -- # mc = Client(servers, debug=1) -- if False: -- -- print("Testing sending a unicode-string key...", end=" ") -- try: -- x = mc.set(six.u('keyhere'), 1) -- except Client.MemcachedStringEncodingError as msg: -- print("OK", end=" ") -- else: -- print("FAIL", end=" ") -- failures += 1 -- try: -- x = mc.set((six.u('a')*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) -- except Client.MemcachedKeyError: -- print("FAIL", end=" ") -- failures += 1 -- else: -- print("OK", end=" ") -- s = pickle.loads('V\\u4f1a\np0\n.') -- try: -- x = mc.set((s * SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) -- except Client.MemcachedKeyLengthError: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing using a value larger than the memcached value limit...") -- print('NOTE: "MemCached: while expecting[...]" is normal...') -- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH) -- if mc.get('keyhere') is None: -- print("OK", end=" ") -- else: -- print("FAIL", end=" ") -- failures += 1 -- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa') -- if mc.get('keyhere') is None: -- print("OK") -- else: -- print("FAIL") -- failures += 1 -- -- print("Testing set_multi() with no memcacheds running", end=" ") -- mc.disconnect_all() -- errors = mc.set_multi({'keyhere': 'a', 'keythere': 'b'}) -- if errors != []: -- print("FAIL") -- failures += 1 -- else: -- print("OK") -- -- print("Testing delete_multi() with no memcacheds running", end=" ") -- mc.disconnect_all() -- ret = mc.delete_multi({'keyhere': 'a', 'keythere': 'b'}) -- if ret != 1: -- print("FAIL") -- failures += 1 -- else: -- print("OK") - -- if failures > 0: -- print('*** THERE WERE FAILED TESTS') -- sys.exit(1) -- sys.exit(0) -+if __name__ == '__main__': -+ unittest.main() ---- python-memcache-1.57.orig/tests/test_setmulti.py -+++ python-memcache-1.57/tests/test_setmulti.py -@@ -53,18 +53,20 @@ class test_Memcached_Set_Multi(unittest. - self.old_socket = socket.socket - socket.socket = FakeSocket - -+ self.mc = memcache.Client(['memcached'], debug=True) -+ - def tearDown(self): - socket.socket = self.old_socket - - def test_Socket_Disconnect(self): -- client = memcache.Client(['memcached'], debug=True) - mapping = {'foo': 'FOO', 'bar': 'BAR'} -- bad_keys = client.set_multi(mapping) -+ bad_keys = self.mc.set_multi(mapping) - - self.assertEqual(sorted(bad_keys), ['bar', 'foo']) - - if DEBUG: - print('set_multi({0!r}) -> {1!r}'.format(mapping, bad_keys)) - -+ - if __name__ == '__main__': - unittest.main() ---- python-memcache-1.57.orig/tox.ini -+++ python-memcache-1.57/tox.ini -@@ -9,7 +9,9 @@ usedevelop = True - install_command = pip install -U {opts} {packages} - deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt --commands = nosetests {posargs} -+commands = -+ nosetests {posargs} -+ python -c 'import memcache; memcache._doctest()' - - [tox:jenkins] - downloadcache = ~/cache/pip diff -Nru python-memcache-1.57/debian/patches/series python-memcache-1.59/debian/patches/series --- python-memcache-1.57/debian/patches/series 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/patches/series 2018-09-05 23:18:05.000000000 +0000 @@ -1,2 +1 @@ using-port-11212-instead-of-standard-11211.patch -More_Python_3_fixes.patch diff -Nru python-memcache-1.57/debian/patches/using-port-11212-instead-of-standard-11211.patch python-memcache-1.59/debian/patches/using-port-11212-instead-of-standard-11211.patch --- python-memcache-1.57/debian/patches/using-port-11212-instead-of-standard-11211.patch 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/patches/using-port-11212-instead-of-standard-11211.patch 2018-09-05 23:18:05.000000000 +0000 @@ -4,16 +4,16 @@ of memcached. Author: Thomas Goirand Forwarded: not-needed -Last-Update: 2015-04-24 +Last-Update: 2018-09-06 --- python-memcache-1.54+20150423+git+48e882719c.orig/tests/test_memcache.py +++ python-memcache-1.54+20150423+git+48e882719c/tests/test_memcache.py -@@ -35,7 +35,7 @@ class FooStruct(object): - class TestMemcache(TestCase): +@@ -27,7 +27,7 @@ + class TestMemcache(unittest.TestCase): def setUp(self): - # TODO: unix socket server stuff + # TODO(): unix socket server stuff - servers = ["127.0.0.1:11211"] + servers = ["127.0.0.1:11212"] self.mc = Client(servers, debug=1) - pass + def tearDown(self): diff -Nru python-memcache-1.57/debian/source/options python-memcache-1.59/debian/source/options --- python-memcache-1.57/debian/source/options 2016-02-27 17:44:10.000000000 +0000 +++ python-memcache-1.59/debian/source/options 2018-09-05 23:18:05.000000000 +0000 @@ -1,3 +1,2 @@ extend-diff-ignore = "^[^/]*[.]egg-info/" extend-diff-ignore = "^Makefile" -extend-diff-ignore = "^[.]gitreview$" diff -Nru python-memcache-1.57/do_release python-memcache-1.59/do_release --- python-memcache-1.57/do_release 1970-01-01 00:00:00.000000000 +0000 +++ python-memcache-1.59/do_release 2017-12-15 04:15:52.000000000 +0000 @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Release the specified version. + +if [ "$#" -ne 1 ]; then + echo "usage: ${0##*/} [VERSION]" + exit 1 +fi + +if [ "$( git status --porcelain | wc -c)" -ne 0 ]; then + echo "ERROR: Outstanding local changes from git." + exit 1 +fi + +VERSION="$1" + +if [ -f /tmp/python-memcached-"$VERSION".tar.gz ]; then + echo "ERROR: Release files already in /tmp" + exit 1 +fi + +sed -ri 's/^(\s*__version__\s*=\s*).*$/\1"'"$VERSION"'"/' memcache.py +sed -ri 's/^(\sVersion:\s*).*$/\1"'"$VERSION"'"/' PKG-INFO + +git tag -s "$VERSION" +git push +git push --tags + +cd /tmp +release "$VERSION" python-memcached +cd python-memcached-"$VERSION" +python setup.py sdist bdist_wheel upload diff -Nru python-memcache-1.57/memcache.py python-memcache-1.59/memcache.py --- python-memcache-1.57/memcache.py 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/memcache.py 2017-12-15 04:15:52.000000000 +0000 @@ -48,8 +48,7 @@ from __future__ import print_function import binascii -import os -import pickle +from io import BytesIO import re import socket import sys @@ -59,11 +58,15 @@ import six +if six.PY2: + # With Python 2, the faster C implementation has to be imported explicitly. + import cPickle as pickle +else: + import pickle + def cmemcache_hash(key): - return ( - (((binascii.crc32(key) & 0xffffffff) - >> 16) & 0x7fff) or 1) + return (((binascii.crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1 serverHashFunction = cmemcache_hash @@ -72,25 +75,13 @@ global serverHashFunction serverHashFunction = binascii.crc32 -from io import BytesIO -if six.PY2: - try: - unicode - except NameError: - _has_unicode = False - else: - _has_unicode = True -else: - _has_unicode = True - -_str_cls = six.string_types valid_key_chars_re = re.compile(b'[\x21-\x7e\x80-\xff]+$') # Original author: Evan Martin of Danga Interactive __author__ = "Sean Reifschneider " -__version__ = "1.57" +__version__ = "1.58" __copyright__ = "Copyright (C) 2003 Danga Interactive" # http://en.wikipedia.org/wiki/Python_Software_Foundation_License __license__ = "Python Software Foundation License" @@ -143,6 +134,7 @@ _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. @@ -258,12 +250,12 @@ return key def _encode_cmd(self, cmd, key, headers, noreply, *args): - cmd_bytes = cmd.encode() if six.PY3 else cmd + cmd_bytes = cmd.encode('utf-8') if six.PY3 else cmd fullcmd = [cmd_bytes, b' ', key] if headers: if six.PY3: - headers = headers.encode() + headers = headers.encode('utf-8') fullcmd.append(b' ') fullcmd.append(headers) @@ -333,12 +325,43 @@ readline = s.readline while 1: line = readline() - if not line or line.strip() == 'END': + if not line or line.decode('ascii').strip() == 'END': break - stats = line.split(' ', 2) + stats = line.decode('ascii').split(' ', 2) serverData[stats[1]] = stats[2] - return(data) + return data + + def get_slab_stats(self): + data = [] + for s in self.servers: + if not s.connect(): + continue + if s.family == socket.AF_INET: + name = '%s:%s (%s)' % (s.ip, s.port, s.weight) + elif s.family == socket.AF_INET6: + name = '[%s]:%s (%s)' % (s.ip, s.port, s.weight) + else: + name = 'unix:%s (%s)' % (s.address, s.weight) + serverData = {} + data.append((name, serverData)) + s.send_cmd('stats slabs') + readline = s.readline + while 1: + line = readline() + if not line or line.strip() == 'END': + break + item = line.split(' ', 2) + if line.startswith('STAT active_slabs') or line.startswith('STAT total_malloced'): + serverData[item[1]] = item[2] + else: + # 0 = STAT, 1 = ITEM, 2 = Value + slab = item[1].split(':', 2) + # 0 = Slab #, 1 = Name + if slab[0] not in serverData: + serverData[slab[0]] = {} + serverData[slab[0]][slab[1]] = item[2] + return data def get_slabs(self): data = [] @@ -410,14 +433,17 @@ if server.connect(): # print("(using server %s)" % server,) return server, key - serverhash = serverHashFunction(str(serverhash) + str(i)) + serverhash = str(serverhash) + str(i) + if isinstance(serverhash, six.text_type): + serverhash = serverhash.encode('ascii') + serverhash = serverHashFunction(serverhash) return None, None def disconnect_all(self): for s in self.servers: s.close_socket() - def delete_multi(self, keys, time=0, key_prefix='', noreply=False): + def delete_multi(self, keys, time=None, key_prefix='', noreply=False): """Delete multiple keys in the memcache doing just one query. >>> notset_keys = mc.set_multi({'a1' : 'val1', 'a2' : 'val2'}) @@ -456,15 +482,15 @@ for server in six.iterkeys(server_keys): bigcmd = [] write = bigcmd.append - extra = ' noreply' if noreply else '' if time is not None: - for key in server_keys[server]: # These are mangled keys - write("delete %s %d%s\r\n" % (key, time, extra)) + headers = str(time) else: - for key in server_keys[server]: # These are mangled keys - write("delete %s%s\r\n" % (key, extra)) + headers = None + for key in server_keys[server]: # These are mangled keys + cmd = self._encode_cmd('delete', key, headers, noreply, b'\r\n') + write(cmd) try: - server.send_cmds(''.join(bigcmd)) + server.send_cmds(b''.join(bigcmd)) except socket.error as msg: rc = 0 if isinstance(msg, tuple): @@ -483,7 +509,7 @@ for server, keys in six.iteritems(server_keys): try: for key in keys: - server.expect("DELETED") + server.expect(b"DELETED") except socket.error as msg: if isinstance(msg, tuple): msg = msg[1] @@ -491,7 +517,7 @@ rc = 0 return rc - def delete(self, key, time=0, noreply=False): + def delete(self, key, time=None, noreply=False): '''Deletes a key from the memcache. @return: Nonzero on success. @@ -527,10 +553,11 @@ if not server: return 0 self._statlog(cmd) - if time is not None and time != 0: - fullcmd = self._encode_cmd(cmd, key, str(time), noreply) + if time is not None: + headers = str(time) else: - fullcmd = self._encode_cmd(cmd, key, None, noreply) + headers = None + fullcmd = self._encode_cmd(cmd, key, headers, noreply) try: server.send_cmd(fullcmd) @@ -540,7 +567,7 @@ if line and line.strip() in expected: return 1 self.debuglog('%s expected %s, got: %r' - % (cmd, ' or '.join(expected), line)) + % (cmd, b' or '.join(expected), line)) except socket.error as msg: if isinstance(msg, tuple): msg = msg[1] @@ -689,9 +716,9 @@ routine. If the value being cached is a string, then the length of the string is measured, else if the value is an object, then the length of the pickle result is measured. If - the resulting attempt at compression yeilds a larger string + the resulting attempt at compression yields a larger string than the input, then it is discarded. For backwards - compatability, this parameter defaults to 0, indicating don't + compatibility, this parameter defaults to 0, indicating don't ever try to compress. @param noreply: optional parameter instructs the server to not @@ -726,9 +753,9 @@ routine. If the value being cached is a string, then the length of the string is measured, else if the value is an object, then the length of the pickle result is measured. If - the resulting attempt at compression yeilds a larger string + the resulting attempt at compression yields a larger string than the input, then it is discarded. For backwards - compatability, this parameter defaults to 0, indicating don't + compatibility, this parameter defaults to 0, indicating don't ever try to compress. @param noreply: optional parameter instructs the server to not @@ -737,7 +764,9 @@ return self._set("cas", key, val, time, min_compress_len, noreply) def _map_and_prefix_keys(self, key_iterable, key_prefix): - """Compute the mapping of server (_Host instance) -> list of keys to + """Map keys to the servers they will reside on. + + Compute the mapping of server (_Host instance) -> list of keys to stuff onto that server, as well as the mapping of prefixed key -> original key. """ @@ -806,9 +835,9 @@ '''Sets multiple keys in the memcache doing just one query. >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'}) - >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', - ... 'key2' : 'val2'} - 1 + >>> keys = mc.get_multi(['key1', 'key2']) + >>> keys == {'key1': 'val1', 'key2': 'val2'} + True This method is recommended over regular L{set} as it lowers @@ -833,9 +862,8 @@ ... key_prefix='subspace_') >>> len(notset_keys) == 0 True - >>> mc.get_multi(['subspace_key1', - ... 'subspace_key2']) == {'subspace_key1': 'val1', - ... 'subspace_key2' : 'val2'} + >>> keys = mc.get_multi(['subspace_key1', 'subspace_key2']) + >>> keys == {'subspace_key1': 'val1', 'subspace_key2': 'val2'} True Causes key 'subspace_key1' and 'subspace_key2' to be @@ -849,9 +877,9 @@ routine. If the value being cached is a string, then the length of the string is measured, else if the value is an object, then the length of the pickle result is - measured. If the resulting attempt at compression yeilds a + measured. If the resulting attempt at compression yields a larger string than the input, then it is discarded. For - backwards compatability, this parameter defaults to 0, + backwards compatibility, this parameter defaults to 0, indicating don't ever try to compress. @param noreply: optional parameter instructs the server to not @@ -905,12 +933,12 @@ # short-circuit if there are no servers, just return all keys if not server_keys: - return(mapping.keys()) + return list(mapping.keys()) for server, keys in six.iteritems(server_keys): try: for key in keys: - if server.readline() == 'STORED': + if server.readline() == b'STORED': continue else: # un-mangle. @@ -928,18 +956,23 @@ 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: val = val.encode('ascii') # force no attempt to compress this silly string. min_compress_len = 0 - elif six.PY2 and isinstance(val, long): + elif six.PY2 and isinstance(val, long): # noqa: F821 flags |= Client._FLAG_LONG val = str(val) if six.PY3: @@ -972,7 +1005,7 @@ # silently do not store if value length exceeds maximum if (self.server_max_value_length != 0 and len(val) > self.server_max_value_length): - return(0) + return 0 return (flags, len(val), val) @@ -993,7 +1026,7 @@ store_info = self._val_to_store_info(val, min_compress_len) if not store_info: - return(0) + return 0 flags, len_val, encoded_val = store_info if cmd == 'cas': @@ -1008,8 +1041,7 @@ server.send_cmd(fullcmd) if noreply: return True - return(server.expect(b"STORED", raise_exception=True) - == b"STORED") + return server.expect(b"STORED", raise_exception=True) == b"STORED" except socket.error as msg: if isinstance(msg, tuple): msg = msg[1] @@ -1039,7 +1071,7 @@ self._statlog(cmd) try: - cmd_bytes = cmd.encode() if six.PY3 else cmd + cmd_bytes = cmd.encode('utf-8') if six.PY3 else cmd fullcmd = b''.join((cmd_bytes, b' ', key)) server.send_cmd(fullcmd) rkey = flags = rlen = cas_id = None @@ -1114,7 +1146,7 @@ ... key_prefix='pfx_') == {'k1' : 1, 'k2' : 2} 1 - get_mult [ and L{set_multi} ] can take str()-ables like ints / + get_multi [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields. They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix. In this mode, the @@ -1146,7 +1178,7 @@ prefix. @return: A dictionary of key/value pairs that were - available. If key_prefix was provided, the keys in the retured + available. If key_prefix was provided, the keys in the returned dictionary will not have it present. ''' @@ -1224,20 +1256,18 @@ 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: if six.PY3: val = int(buf) else: - val = long(buf) + val = long(buf) # noqa: F821 elif flags & Client._FLAG_PICKLE: try: file = BytesIO(buf) @@ -1434,9 +1464,11 @@ if self.debug and line != text: if six.PY3: text = text.decode('utf8') - line = line.decode('utf8', 'replace') + log_line = line.decode('utf8', 'replace') + else: + log_line = line self.debuglog("while expecting %r, got unexpected response %r" - % (text, line)) + % (text, log_line)) return line def recv(self, rlen): @@ -1472,236 +1504,13 @@ import doctest import memcache servers = ["127.0.0.1:11211"] - mc = Client(servers, debug=1) + mc = memcache.Client(servers, debug=1) globs = {"mc": mc} - return doctest.testmod(memcache, globs=globs) - -if __name__ == "__main__": - failures = 0 - print("Testing docstrings...") - _doctest() - print("Running tests:") - print() - serverList = [["127.0.0.1:11211"]] - if '--do-unix' in sys.argv: - serverList.append([os.path.join(os.getcwd(), 'memcached.socket')]) - - for servers in serverList: - mc = Client(servers, debug=1) - - def to_s(val): - if not isinstance(val, _str_cls): - return "%s (%s)" % (val, type(val)) - return "%s" % val - - def test_setget(key, val, noreply=False): - global failures - print("Testing set/get (noreply=%s) {'%s': %s} ..." - % (noreply, to_s(key), to_s(val)), end=" ") - mc.set(key, val, noreply=noreply) - newval = mc.get(key) - if newval == val: - print("OK") - return 1 - else: - print("FAIL") - failures += 1 - return 0 - - class FooStruct(object): - - def __init__(self): - self.bar = "baz" - - def __str__(self): - return "A FooStruct" - - def __eq__(self, other): - if isinstance(other, FooStruct): - return self.bar == other.bar - return 0 - - test_setget("a_string", "some random string") - test_setget("a_string_2", "some random string", noreply=True) - test_setget("an_integer", 42) - test_setget("an_integer_2", 42, noreply=True) - if six.PY3: - ok = test_setget("long", 1 << 30) - else: - ok = test_setget("long", long(1 << 30)) - if ok: - print("Testing delete ...", end=" ") - if mc.delete("long"): - print("OK") - else: - print("FAIL") - failures += 1 - print("Checking results of delete ...", end=" ") - if mc.get("long") is None: - print("OK") - else: - print("FAIL") - failures += 1 - print("Testing get_multi ...",) - print(mc.get_multi(["a_string", "an_integer", "a_string_2", - "an_integer_2"])) - - # removed from the protocol - # if test_setget("timed_delete", 'foo'): - # print "Testing timed delete ...", - # if mc.delete("timed_delete", 1): - # print("OK") - # else: - # print("FAIL") - # failures += 1 - # print "Checking results of timed delete ..." - # if mc.get("timed_delete") is None: - # print("OK") - # else: - # print("FAIL") - # failures += 1 - - print("Testing get(unknown value) ...", end=" ") - print(to_s(mc.get("unknown_value"))) - - f = FooStruct() - test_setget("foostruct", f) - test_setget("foostruct_2", f, noreply=True) - - print("Testing incr ...", end=" ") - x = mc.incr("an_integer", 1) - if x == 43: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing incr (noreply=True) ...", end=" ") - mc.incr("an_integer_2", 1, noreply=True) - x = mc.get("an_integer_2") - if x == 43: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing decr ...", end=" ") - x = mc.decr("an_integer", 1) - if x == 42: - print("OK") - else: - print("FAIL") - failures += 1 - sys.stdout.flush() - - print("Testing decr (noreply=True) ...", end=" ") - mc.decr("an_integer_2", 1, noreply=True) - x = mc.get("an_integer_2") - if x == 42: - print("OK") - else: - print("FAIL") - failures += 1 - sys.stdout.flush() - - # sanity tests - print("Testing sending spaces...", end=" ") - sys.stdout.flush() - try: - x = mc.set("this has spaces", 1) - except Client.MemcachedKeyCharacterError as msg: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing sending control characters...", end=" ") - try: - x = mc.set("this\x10has\x11control characters\x02", 1) - except Client.MemcachedKeyCharacterError as msg: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing using insanely long key...", end=" ") - try: - x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1) - x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1, noreply=True) - except Client.MemcachedKeyLengthError as msg: - print("FAIL") - failures += 1 - else: - print("OK") - try: - x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'a', 1) - except Client.MemcachedKeyLengthError as msg: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing sending a unicode-string key...", end=" ") - try: - x = mc.set(unicode('keyhere'), 1) - except Client.MemcachedStringEncodingError as msg: - print("OK", end=" ") - else: - print("FAIL", end=" ") - failures += 1 - try: - x = mc.set((unicode('a')*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) - except Client.MemcachedKeyError: - print("FAIL", end=" ") - failures += 1 - else: - print("OK", end=" ") - s = pickle.loads('V\\u4f1a\np0\n.') - try: - x = mc.set((s * SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) - except Client.MemcachedKeyLengthError: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing using a value larger than the memcached value limit...") - print('NOTE: "MemCached: while expecting[...]" is normal...') - x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH) - if mc.get('keyhere') is None: - print("OK", end=" ") - else: - print("FAIL", end=" ") - failures += 1 - x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa') - if mc.get('keyhere') is None: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing set_multi() with no memcacheds running", end=" ") - mc.disconnect_all() - errors = mc.set_multi({'keyhere': 'a', 'keythere': 'b'}) - if errors != []: - print("FAIL") - failures += 1 - else: - print("OK") - - print("Testing delete_multi() with no memcacheds running", end=" ") - mc.disconnect_all() - ret = mc.delete_multi({'keyhere': 'a', 'keythere': 'b'}) - if ret != 1: - print("FAIL") - failures += 1 - else: - print("OK") - - if failures > 0: - print('*** THERE WERE FAILED TESTS') + results = doctest.testmod(memcache, globs=globs) + mc.disconnect_all() + print("Doctests: %s" % (results,)) + if results.failed: sys.exit(1) - sys.exit(0) # vim: ts=4 sw=4 et : diff -Nru python-memcache-1.57/PKG-INFO python-memcache-1.59/PKG-INFO --- python-memcache-1.57/PKG-INFO 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/PKG-INFO 2017-12-15 04:15:52.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: python-memcached -Version: 1.31 +Version: 1.59 Summary: A Python memcached client library. Home-page: http://www.tummy.com/Community/software/python-memcached/ Author: Sean Reifschneider diff -Nru python-memcache-1.57/releasescript.auto python-memcache-1.59/releasescript.auto --- python-memcache-1.57/releasescript.auto 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/releasescript.auto 2017-12-15 04:15:52.000000000 +0000 @@ -2,16 +2,4 @@ # # Run when the .tar file is created, updates the .spec file. -dir=`pwd` -rev=${dir##*-} - -echo "Updating files to $rev" -sed --in-place 's/^Version: .*$/Version: '"$rev"/ PKG-INFO -sed --in-place 's/^__version__ .*$/__version__ = "'"$rev"'"/' memcache.py - -pydoc -w ./memcache.py - -rm -f releasescript.auto - -python setup.py sdist bdist_wheel upload -rm -rf *.pyc dist python_memcached.egg-info +# All done in "do_release" now. diff -Nru python-memcache-1.57/requirements.txt python-memcache-1.59/requirements.txt --- python-memcache-1.57/requirements.txt 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/requirements.txt 2017-12-15 04:15:52.000000000 +0000 @@ -1 +1 @@ -six +six>=1.4.0 diff -Nru python-memcache-1.57/setup.cfg python-memcache-1.59/setup.cfg --- python-memcache-1.57/setup.cfg 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/setup.cfg 2017-12-15 04:15:52.000000000 +0000 @@ -3,5 +3,8 @@ packager = Sean Reifschneider requires = python-memcached +[flake8] +ignore = H304,H405 + [wheel] universal = 1 diff -Nru python-memcache-1.57/setup.py python-memcache-1.59/setup.py --- python-memcache-1.57/setup.py 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/setup.py 2017-12-15 04:15:52.000000000 +0000 @@ -1,34 +1,36 @@ #!/usr/bin/env python +from setuptools.depends import get_module_constant from setuptools import setup # noqa -setup(name="python-memcached", - version="1.53", - description="Pure python memcached client", - long_description=open("README.md").read(), - author="Evan Martin", - author_email="martine@danga.com", - maintainer="Sean Reifschneider", - maintainer_email="jafo@tummy.com", - url="http://www.tummy.com/Community/software/python-memcached/", - download_url="ftp://ftp.tummy.com/pub/python-memcached/", - py_modules=["memcache"], - install_requires=open('requirements.txt').read().split(), - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Python Software Foundation License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Topic :: Internet", - "Topic :: Software Development :: Libraries :: Python Modules", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4" - ]) +setup( + name="python-memcached", + version=get_module_constant('memcache', '__version__'), + description="Pure python memcached client", + long_description=open("README.md").read(), + author="Evan Martin", + author_email="martine@danga.com", + maintainer="Sean Reifschneider", + maintainer_email="jafo@tummy.com", + url="http://www.tummy.com/Community/software/python-memcached/", + download_url="ftp://ftp.tummy.com/pub/python-memcached/", + py_modules=["memcache"], + install_requires=open('requirements.txt').read().split(), + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Python Software Foundation License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Internet", + "Topic :: Software Development :: Libraries :: Python Modules", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + ], +) diff -Nru python-memcache-1.57/test-requirements.txt python-memcache-1.59/test-requirements.txt --- python-memcache-1.57/test-requirements.txt 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/test-requirements.txt 2017-12-15 04:15:52.000000000 +0000 @@ -1,3 +1,4 @@ nose coverage hacking +mock diff -Nru python-memcache-1.57/tests/test_memcache.py python-memcache-1.59/tests/test_memcache.py --- python-memcache-1.57/tests/test_memcache.py 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/tests/test_memcache.py 2017-12-15 04:15:52.000000000 +0000 @@ -1,21 +1,13 @@ +# -*- coding: utf-8 -*- from __future__ import print_function -from unittest import TestCase +import unittest +import zlib -import six +import mock -from memcache import Client, SERVER_MAX_KEY_LENGTH - -try: - _str_cls = basestring -except NameError: - _str_cls = str - - -def to_s(val): - if not isinstance(val, _str_cls): - return "%s (%s)" % (val, type(val)) - return "%s" % val +from memcache import Client, _Host, SERVER_MAX_KEY_LENGTH, SERVER_MAX_VALUE_LENGTH # noqa: H301 +from .utils import captured_stderr class FooStruct(object): @@ -32,12 +24,15 @@ return 0 -class TestMemcache(TestCase): +class TestMemcache(unittest.TestCase): def setUp(self): - # TODO: unix socket server stuff + # TODO(): unix socket server stuff servers = ["127.0.0.1:11211"] self.mc = Client(servers, debug=1) - pass + + def tearDown(self): + self.mc.flush_all() + self.mc.disconnect_all() def check_setget(self, key, val, noreply=False): self.mc.set(key, val, noreply=noreply) @@ -56,6 +51,13 @@ self.assertEqual(result, True) self.assertEqual(self.mc.get("long"), None) + @mock.patch.object(_Host, 'send_cmd') + @mock.patch.object(_Host, 'readline') + def test_touch(self, mock_readline, mock_send_cmd): + with captured_stderr(): + self.mc.touch('key') + mock_send_cmd.assert_called_with(b'touch key 0') + def test_get_multi(self): self.check_setget("gm_a_string", "some random string") self.check_setget("gm_an_integer", 42) @@ -64,6 +66,8 @@ {"gm_an_integer": 42, "gm_a_string": "some random string"}) def test_get_unknown_value(self): + self.mc.delete("unknown_value") + self.assertEqual(self.mc.get("unknown_value"), None) def test_setget_foostruct(self): @@ -120,79 +124,113 @@ self.mc.set('a' * SERVER_MAX_KEY_LENGTH, 1) self.mc.set('a' * SERVER_MAX_KEY_LENGTH, 1, noreply=True) + def test_setget_boolean(self): + """GitHub issue #75. Set/get with boolean values.""" + self.check_setget("bool", True) + + def test_unicode_key(self): + s = u'\u4f1a' + maxlen = SERVER_MAX_KEY_LENGTH // len(s.encode('utf-8')) + key = s * maxlen + + self.mc.set(key, 5) + 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' + + value = 'a' * (SERVER_MAX_VALUE_LENGTH // 2) + self.assertTrue(self.mc.set(key, value)) + self.assertEqual(self.mc.get(key), value) + + value = 'a' * SERVER_MAX_VALUE_LENGTH + with captured_stderr() as log: + self.assertIs(self.mc.set(key, value), False) + self.assertEqual( + log.getvalue(), + "MemCached: while expecting 'STORED', got unexpected response " + "'SERVER_ERROR object too large for cache'\n" + ) + # This test fails if the -I option is used on the memcached server + self.assertTrue(self.mc.get(key) is None) + + def test_get_set_multi_key_prefix(self): + """Testing set_multi() with no memcacheds running.""" + + prefix = 'pfx_' + values = {'key1': 'a', 'key2': 'b'} + errors = self.mc.set_multi(values, key_prefix=prefix) + self.assertEqual(errors, []) + + keys = list(values) + self.assertEqual(self.mc.get_multi(keys, key_prefix=prefix), + values) + + def test_set_multi_dead_servers(self): + """Testing set_multi() with no memcacheds running.""" + + self.mc.disconnect_all() + with captured_stderr() as log: + for server in self.mc.servers: + server.mark_dead('test') + self.assertIn('Marking dead.', log.getvalue()) + errors = self.mc.set_multi({'key1': 'a', 'key2': 'b'}) + self.assertEqual(sorted(errors), ['key1', 'key2']) + + def test_disconnect_all_delete_multi(self): + """Testing delete_multi() with no memcacheds running.""" + self.mc.disconnect_all() + with captured_stderr() as output: + ret = self.mc.delete_multi(('keyhere', 'keythere')) + self.assertEqual(ret, 1) + self.assertEqual( + output.getvalue(), + "MemCached: while expecting 'DELETED', got unexpected response " + "'NOT_FOUND'\n" + "MemCached: while expecting 'DELETED', got unexpected response " + "'NOT_FOUND'\n" + ) + + @mock.patch.object(_Host, 'send_cmd') # Don't send any commands. + @mock.patch.object(_Host, 'readline') + def test_touch_unexpected_reply(self, mock_readline, mock_send_cmd): + """touch() logs an error upon receiving an unexpected reply.""" + mock_readline.return_value = 'SET' # the unexpected reply + with captured_stderr() as output: + self.mc.touch('key') + self.assertEqual( + output.getvalue(), + "MemCached: touch expected %s, got: 'SET'\n" % b'TOUCHED' + ) -if __name__ == "__main__": - # failures = 0 - # print("Testing docstrings...") - # _doctest() - # print("Running tests:") - # print() - # serverList = [["127.0.0.1:11211"]] - # if '--do-unix' in sys.argv: - # serverList.append([os.path.join(os.getcwd(), 'memcached.socket')]) - - # for servers in serverList: - # mc = Client(servers, debug=1) - if False: - - print("Testing sending a unicode-string key...", end=" ") - try: - x = mc.set(six.u('keyhere'), 1) - except Client.MemcachedStringEncodingError as msg: - print("OK", end=" ") - else: - print("FAIL", end=" ") - failures += 1 - try: - x = mc.set((six.u('a')*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) - except Client.MemcachedKeyError: - print("FAIL", end=" ") - failures += 1 - else: - print("OK", end=" ") - s = pickle.loads('V\\u4f1a\np0\n.') - try: - x = mc.set((s * SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1) - except Client.MemcachedKeyLengthError: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing using a value larger than the memcached value limit...") - print('NOTE: "MemCached: while expecting[...]" is normal...') - x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH) - if mc.get('keyhere') is None: - print("OK", end=" ") - else: - print("FAIL", end=" ") - failures += 1 - x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa') - if mc.get('keyhere') is None: - print("OK") - else: - print("FAIL") - failures += 1 - - print("Testing set_multi() with no memcacheds running", end=" ") - mc.disconnect_all() - errors = mc.set_multi({'keyhere': 'a', 'keythere': 'b'}) - if errors != []: - print("FAIL") - failures += 1 - else: - print("OK") - - print("Testing delete_multi() with no memcacheds running", end=" ") - mc.disconnect_all() - ret = mc.delete_multi({'keyhere': 'a', 'keythere': 'b'}) - if ret != 1: - print("FAIL") - failures += 1 - else: - print("OK") - if failures > 0: - print('*** THERE WERE FAILED TESTS') - sys.exit(1) - sys.exit(0) +if __name__ == '__main__': + unittest.main() diff -Nru python-memcache-1.57/tests/test_setmulti.py python-memcache-1.59/tests/test_setmulti.py --- python-memcache-1.57/tests/test_setmulti.py 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/tests/test_setmulti.py 2017-12-15 04:15:52.000000000 +0000 @@ -13,8 +13,10 @@ import sys import unittest +from .utils import captured_stderr + sys.path.append('..') -import memcache +import memcache # noqa: E402 DEBUG = False @@ -53,18 +55,20 @@ self.old_socket = socket.socket socket.socket = FakeSocket + self.mc = memcache.Client(['memcached'], debug=True) + def tearDown(self): socket.socket = self.old_socket def test_Socket_Disconnect(self): - client = memcache.Client(['memcached'], debug=True) mapping = {'foo': 'FOO', 'bar': 'BAR'} - bad_keys = client.set_multi(mapping) - + with captured_stderr() as log: + bad_keys = self.mc.set_multi(mapping) + self.assertIn('connection closed in readline().', log.getvalue()) self.assertEqual(sorted(bad_keys), ['bar', 'foo']) - if DEBUG: print('set_multi({0!r}) -> {1!r}'.format(mapping, bad_keys)) + if __name__ == '__main__': unittest.main() diff -Nru python-memcache-1.57/tests/utils.py python-memcache-1.59/tests/utils.py --- python-memcache-1.57/tests/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ python-memcache-1.59/tests/utils.py 2017-12-15 04:15:52.000000000 +0000 @@ -0,0 +1,30 @@ +from contextlib import contextmanager +import sys + +from six import StringIO + + +@contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + This function and the following ``captured_std*`` are copied + from CPython's ``test.support`` module. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StringIO()) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stderr(): + """Capture the output of sys.stderr: + + with captured_stderr() as stderr: + print('hello', file=sys.stderr) + self.assertEqual(stderr.getvalue(), 'hello\n') + """ + return captured_output('stderr') diff -Nru python-memcache-1.57/tox.ini python-memcache-1.59/tox.ini --- python-memcache-1.57/tox.ini 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/tox.ini 2017-12-15 04:15:52.000000000 +0000 @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py26,py27,py32,py33,py34,pypy,pep8 +envlist = py27,py34,py35,p36,pypy,pep8 skipsdist = True [testenv] @@ -9,7 +9,9 @@ install_command = pip install -U {opts} {packages} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = nosetests {posargs} +commands = + nosetests {posargs} + python -c 'import memcache; memcache._doctest()' [tox:jenkins] downloadcache = ~/cache/pip @@ -21,4 +23,5 @@ commands = nosetests --with-coverage {posargs} [flake8] -exclude = .venv*,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*.egg,.update-venv +exclude = .venv*,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*.egg,.update-venv,build +max-line-length = 119 diff -Nru python-memcache-1.57/.travis.yml python-memcache-1.59/.travis.yml --- python-memcache-1.57/.travis.yml 2015-07-31 17:46:13.000000000 +0000 +++ python-memcache-1.59/.travis.yml 2017-12-15 04:15:52.000000000 +0000 @@ -1,13 +1,15 @@ language: python python: - - 2.6 - 2.7 - - 3.2 - - 3.3 - 3.4 + - 3.5 + - 3.6 - pypy services: - memcached install: python setup.py install -before_script: pip install nose -script: nosetests +before_script: pip install -r test-requirements.txt +script: + - flake8 + - nosetests + - python -c 'import memcache; memcache._doctest()'