Hello community, here is the log from the commit of package python-pylibmc for openSUSE:Factory checked in at 2019-03-14 14:55:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pylibmc (Old) and /work/SRC/openSUSE:Factory/.python-pylibmc.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pylibmc" Thu Mar 14 14:55:35 2019 rev:6 rq:684213 version:1.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pylibmc/python-pylibmc.changes 2017-09-05 15:19:33.456656110 +0200 +++ /work/SRC/openSUSE:Factory/.python-pylibmc.new.28833/python-pylibmc.changes 2019-03-14 14:55:36.947758249 +0100 @@ -1,0 +2,9 @@ +Tue Mar 12 05:07:50 UTC 2019 - John Vandenberg <[email protected]> + +- Remove bcond test +- Activate test suite +- Use %license +- Update URL to GitHub repository +- Update to v1.6.0 + +------------------------------------------------------------------- Old: ---- pylibmc-1.5.2.tar.gz New: ---- pylibmc-1.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pylibmc.spec ++++++ --- /var/tmp/diff_new_pack.9SljKH/_old 2019-03-14 14:55:37.599758146 +0100 +++ /var/tmp/diff_new_pack.9SljKH/_new 2019-03-14 14:55:37.603758145 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pylibmc # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,29 +12,27 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} -%bcond_with test Name: python-pylibmc -Version: 1.5.2 +Version: 1.6.0 Release: 0 Summary: memcached client for Python License: BSD-3-Clause Group: Development/Languages/Python -Url: http://sendapatch.se/projects/pylibmc/ +URL: https://github.com/lericson/pylibmc Source: https://files.pythonhosted.org/packages/source/p/pylibmc/pylibmc-%{version}.tar.gz BuildRequires: %{python_module devel} +BuildRequires: %{python_module nose} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: libmemcached-devel +BuildRequires: memcached BuildRequires: python-rpm-macros BuildRequires: zlib-devel -%if %{with test} -BuildRequires: %{python_module nose} -%endif %python_subpackages %description @@ -56,17 +54,18 @@ %python_install %python_expand %fdupes %{buildroot}%{$python_sitearch} -%if %{with test} %check +%{_sbindir}/memcached & +pid=$! export NOSE_IGNORE_CONFIG_FILES=1 %{python_expand export PYTHONPATH=%{buildroot}%{$python_sitearch} nosetests-%{$python_bin_suffix} tests } -%endif +kill $pid %files %{python_files} -%defattr(-,root,root,-) -%doc LICENSE README.rst +%license LICENSE +%doc README.rst %{python_sitearch}/* %changelog ++++++ pylibmc-1.5.2.tar.gz -> pylibmc-1.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/PKG-INFO new/pylibmc-1.6.0/PKG-INFO --- old/pylibmc-1.5.2/PKG-INFO 2017-03-06 14:13:31.000000000 +0100 +++ new/pylibmc-1.6.0/PKG-INFO 2018-11-09 19:05:45.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pylibmc -Version: 1.5.2 +Version: 1.6.0 Summary: Quick and small memcached client for Python Home-page: http://sendapatch.se/projects/pylibmc/ Author: Ludvig Ericson @@ -15,6 +15,22 @@ .. image:: https://travis-ci.org/lericson/pylibmc.png?branch=master :target: https://travis-ci.org/lericson/pylibmc + New in version 1.6.0 + ==================== + + Though no major feature overhauls have taken place, this release is partially + incompatible with 1.5.0. This stems from the fact that python-memcached is now + using a flag that pylibmc has been using for some years. python-memcached uses + it for a different purpose, and an incompatible one. We deemed that it would be + better to support this interoperability. The change also means that Unicode + strings are now stored as UTF-8 rather than pickled, which may or may not + result in a slight performance improvement for this type of data. + + We have also introduced a `pickle_protocol` behavior to enable seamless + interoperability between Python 2.x and 3.x. Also, this release introduces a + ManyLinux wheel, making installation a breeze on ManyLinux systems (which I + suppose is many linuxes.) + New in version 1.5.0 ==================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/README.rst new/pylibmc-1.6.0/README.rst --- old/pylibmc-1.5.2/README.rst 2016-03-21 19:38:38.000000000 +0100 +++ new/pylibmc-1.6.0/README.rst 2018-11-09 18:49:56.000000000 +0100 @@ -7,6 +7,22 @@ .. image:: https://travis-ci.org/lericson/pylibmc.png?branch=master :target: https://travis-ci.org/lericson/pylibmc +New in version 1.6.0 +==================== + +Though no major feature overhauls have taken place, this release is partially +incompatible with 1.5.0. This stems from the fact that python-memcached is now +using a flag that pylibmc has been using for some years. python-memcached uses +it for a different purpose, and an incompatible one. We deemed that it would be +better to support this interoperability. The change also means that Unicode +strings are now stored as UTF-8 rather than pickled, which may or may not +result in a slight performance improvement for this type of data. + +We have also introduced a `pickle_protocol` behavior to enable seamless +interoperability between Python 2.x and 3.x. Also, this release introduces a +ManyLinux wheel, making installation a breeze on ManyLinux systems (which I +suppose is many linuxes.) + New in version 1.5.0 ==================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/docs/behaviors.rst new/pylibmc-1.6.0/docs/behaviors.rst --- old/pylibmc-1.5.2/docs/behaviors.rst 2015-09-28 18:38:56.000000000 +0200 +++ new/pylibmc-1.6.0/docs/behaviors.rst 2018-11-09 18:42:08.000000000 +0100 @@ -250,3 +250,18 @@ ``auto_eject``; these still exist, but their interaction with the state machine is unclear, and should be avoided. ``remove_failed`` acts as a combination of the two. + +Non-libmemcached Behaviors +-------------------------- + +It wouldn't make sense to have multiple avenues of configuration, and so +whenever possible, pylibmc tries to co-opt the behaviors dictionary for +configuration of strictly pylibmc-level things. These are described below. + +.. _pickle_protocol: + +``"pickle_protocol"`` + Specifies the default pickling protocol. This is by default set to -1, which + means the pickle module will use the latest protocol it understands. This is + an issue for interoperability, and so for example to work between Python 2 + and 3, set this explicitly to 2 or whatever you prefer. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/docs/install.rst new/pylibmc-1.6.0/docs/install.rst --- old/pylibmc-1.5.2/docs/install.rst 2016-06-13 16:18:53.000000000 +0200 +++ new/pylibmc-1.6.0/docs/install.rst 2018-11-09 18:42:08.000000000 +0100 @@ -5,8 +5,8 @@ Requirements ============ -* Python 2.5, Python 3.2 or later -* libmemcached 0.32 or later (last test with 1.0.18) +* Python 2.6-2.7, or Python 3.3-3.6 +* libmemcached 1.0.8 or later (latest tested is 1.0.18) * zlib (required for compression support) * libsasl2 (required for authentication support) @@ -28,7 +28,11 @@ From PyPI --------- -Since ``pip`` doesn't support passing arguments to the setup script, -you can also define environment variables:: +Using ``pip`` you can pass install options as follows:: - LIBMEMCACHED=/opt/local pip install pylibmc + pip install pylibmc --install-option="--with-libmemcached=/usr/local/" + +Using Homebrew (MacOSX) you can install from PyPI via:: + + brew install libmemcached + pip install pylibmc --install-option="--with-libmemcached=/usr/local/Cellar/libmemcached" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/docs/reference.rst new/pylibmc-1.6.0/docs/reference.rst --- old/pylibmc-1.5.2/docs/reference.rst 2016-08-09 18:06:26.000000000 +0200 +++ new/pylibmc-1.6.0/docs/reference.rst 2018-11-09 18:42:08.000000000 +0100 @@ -180,13 +180,13 @@ .. method:: touch(key, time) -> touched - Touch a given *key* and increase it's expiry time by *time* seconds. + Touch a given *key* and set its expiry time to *time* seconds. :param key: Key to touch :param time: Number of seconds until the key expires. Returns ``True`` if the key was successfully touched. ``False`` - if the key did not exist. + if the key did not exist (so touching is not possible.) .. Utilities diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/setup.py new/pylibmc-1.6.0/setup.py --- old/pylibmc-1.5.2/setup.py 2016-08-09 18:06:26.000000000 +0200 +++ new/pylibmc-1.6.0/setup.py 2018-11-09 18:42:08.000000000 +0100 @@ -76,14 +76,14 @@ # strict aliasing rules. Let's skip this flag for now. cflags = ["-fno-strict-aliasing", "-std=c99"] -## Extension definitions +# Extension definitions pylibmc_ext = Extension("_pylibmc", ["src/_pylibmcmodule.c"], libraries=libs, include_dirs=incdirs, library_dirs=libdirs, define_macros=defs, extra_compile_args=cflags) -# Hidden secret: if environment variable GEN_SETUP is set, generate Setup file. +# Hidden secret: generate Setup file for statically compiling the extension. if cmd == "gen-setup": line = " ".join(( pylibmc_ext.name, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/src/_pylibmcmodule.c new/pylibmc-1.6.0/src/_pylibmcmodule.c --- old/pylibmc-1.5.2/src/_pylibmcmodule.c 2017-02-25 17:39:03.000000000 +0100 +++ new/pylibmc-1.6.0/src/_pylibmcmodule.c 2018-11-09 18:42:08.000000000 +0100 @@ -216,6 +216,8 @@ } self->native_deserialization = (uint8_t) native_deserialization; + self->pickle_protocol = -1; + while ((c_srv = PyIter_Next(srvs_it)) != NULL) { unsigned char stype; char *hostname; @@ -352,9 +354,8 @@ goto error; } - if(strm.total_out >= value_len) { - /* if we didn't actually save anything, don't bother storing it - compressed */ + if ((Py_ssize_t)strm.total_out >= value_len) { + /* if no data was saved, don't use compression */ goto error; } @@ -594,7 +595,7 @@ #else if (flags & PYLIBMC_FLAG_ZLIB) { PyErr_SetString(PylibMCExc_Error, - "value for key compressed, unable to inflate"); + "key is compressed but pylibmc is compiled without zlib support"); return NULL; } #endif @@ -645,20 +646,21 @@ switch (dtype) { case PYLIBMC_FLAG_PICKLE: - retval = value ? _PylibMC_Unpickle_Bytes(value) : _PylibMC_Unpickle(value_str, value_size); + retval = value ? _PylibMC_Unpickle_Bytes(self, value) : _PylibMC_Unpickle(self, value_str, value_size); break; case PYLIBMC_FLAG_INTEGER: case PYLIBMC_FLAG_LONG: - case PYLIBMC_FLAG_BOOL: if (value) { retval = PyLong_FromString(PyBytes_AS_STRING(value), NULL, 10); } else { retval = _PyLong_FromStringAndSize(value_str, value_size, NULL, 10);; } - if (retval != NULL && dtype == PYLIBMC_FLAG_BOOL) { - PyObject *bool_retval = PyBool_FromLong(PyLong_AS_LONG(retval)); - Py_DECREF(retval); - retval = bool_retval; + break; + case PYLIBMC_FLAG_TEXT: + if (value) { + retval = PyUnicode_FromEncodedObject(value, "utf-8", "strict"); + } else { + retval = PyUnicode_FromStringAndSize(value_str, value_size); } break; case PYLIBMC_FLAG_NONE: @@ -723,7 +725,7 @@ if (!_key_normalized_obj(&key)) { return NULL; - } else if (!PySequence_Length(key) ) { + } else if (!PySequence_Length(key)) { Py_INCREF(default_value); return default_value; } @@ -736,18 +738,23 @@ Py_DECREF(key); - if (mc_val != NULL) { + if (error == MEMCACHED_SUCCESS) { + /* note that mc_val can and is NULL for zero-length values. */ PyObject *r = _PylibMC_parse_memcached_value(self, mc_val, val_size, flags); - free(mc_val); + + if (mc_val != NULL) { + free(mc_val); + } + if (_PylibMC_cache_miss_simulated(r)) { Py_INCREF(default_value); return default_value; } + return r; - } else if (error == MEMCACHED_SUCCESS) { - /* This happens for empty values, and so we fake an empty string. */ - return PyBytes_FromStringAndSize("", 0); - } else if (error == MEMCACHED_NOTFOUND) { + } + + if (error == MEMCACHED_NOTFOUND) { Py_INCREF(default_value); return default_value; } @@ -1247,12 +1254,12 @@ /* Make store_val an owned reference */ store_val = value_obj; Py_INCREF(store_val); + } else if (PyUnicode_Check(value_obj)) { + store_flags = PYLIBMC_FLAG_TEXT; + store_val = PyUnicode_AsUTF8String(value_obj); } else if (PyBool_Check(value_obj)) { - store_flags |= PYLIBMC_FLAG_BOOL; - /* bool cannot be subclassed; there are only two singleton values, - Py_True and Py_False */ - const char *value_str = (value_obj == Py_True) ? "1" : "0"; - store_val = PyBytes_FromString(value_str); + store_flags |= PYLIBMC_FLAG_INTEGER; + store_val = PyBytes_FromStringAndSize(&"01"[value_obj == Py_True], 1); #if PY_MAJOR_VERSION >= 3 } else if (PyLong_Check(value_obj)) { store_flags |= PYLIBMC_FLAG_LONG; @@ -1275,7 +1282,7 @@ /* we have no idea what it is, so we'll store it pickled */ Py_INCREF(value_obj); store_flags |= PYLIBMC_FLAG_PICKLE; - store_val = _PylibMC_Pickle(value_obj); + store_val = _PylibMC_Pickle(self, value_obj); Py_DECREF(value_obj); } @@ -2128,14 +2135,21 @@ uint64_t bval; PyObject *x; - bval = memcached_behavior_get(self->mc, b->flag); + switch (b->flag) { + case PYLIBMC_BEHAVIOR_PICKLE_PROTOCOL: + bval = self->pickle_protocol; + break; + default: + bval = memcached_behavior_get(self->mc, b->flag); + } + x = PyLong_FromLong((long)bval); if (x == NULL || PyDict_SetItemString(retval, b->name, x) == -1) { Py_XDECREF(x); goto error; } - Py_DECREF(x); + } return retval; @@ -2148,7 +2162,7 @@ PyObject *behaviors) { PylibMC_Behavior *b; PyObject *py_v; - uint64_t v; + long v; memcached_return r; char *key; @@ -2163,16 +2177,23 @@ goto error; } - v = (uint64_t)PyLong_AS_LONG(py_v); + v = PyLong_AsLong(py_v); Py_DECREF(py_v); - r = memcached_behavior_set(self->mc, b->flag, v); - if (r != MEMCACHED_SUCCESS) { - PyErr_Format(PylibMCExc_Error, - "memcached_behavior_set returned %d for " - "behavior '%.32s' = %u", r, b->name, (unsigned int)v); - goto error; + switch (b->flag) { + case PYLIBMC_BEHAVIOR_PICKLE_PROTOCOL: + self->pickle_protocol = v; + break; + default: + r = memcached_behavior_set(self->mc, b->flag, (uint64_t)v); + if (r != MEMCACHED_SUCCESS) { + PyErr_Format(PylibMCExc_Error, + "memcached_behavior_set returned %d for " + "behavior '%.32s' = %ld", r, b->name, v); + goto error; + } } + } for (b = PylibMC_callbacks; b->name != NULL; b++) { @@ -2383,6 +2404,7 @@ Py_END_ALLOW_THREADS; clone->native_serialization = self->native_serialization; clone->native_deserialization = self->native_deserialization; + clone->pickle_protocol = self->pickle_protocol; return (PyObject *)clone; } /* }}} */ @@ -2471,7 +2493,7 @@ return pickle_attr; } -static PyObject *_PylibMC_Unpickle(const char *buff, Py_ssize_t size) { +static PyObject *_PylibMC_Unpickle(PylibMC_Client *self, const char *buff, Py_ssize_t size) { #if PY_MAJOR_VERSION >= 3 return PyObject_CallFunction(_PylibMC_pickle_loads, "y#", buff, size); #else @@ -2479,12 +2501,12 @@ #endif } -static PyObject *_PylibMC_Unpickle_Bytes(PyObject *val) { +static PyObject *_PylibMC_Unpickle_Bytes(PylibMC_Client *self, PyObject *val) { return PyObject_CallFunctionObjArgs(_PylibMC_pickle_loads, val, NULL); } -static PyObject *_PylibMC_Pickle(PyObject *val) { - return PyObject_CallFunction(_PylibMC_pickle_dumps, "Oi", val, -1); +static PyObject *_PylibMC_Pickle(PylibMC_Client *self, PyObject *val) { + return PyObject_CallFunction(_PylibMC_pickle_dumps, "Oi", val, self->pickle_protocol); } /* }}} */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/src/_pylibmcmodule.h new/pylibmc-1.6.0/src/_pylibmcmodule.h --- old/pylibmc-1.5.2/src/_pylibmcmodule.h 2016-08-09 18:06:26.000000000 +0200 +++ new/pylibmc-1.6.0/src/_pylibmcmodule.h 2018-11-09 18:42:08.000000000 +0100 @@ -60,23 +60,29 @@ /* {{{ Key flags from python-memcached * Some flags (like the compression one, ZLIB) are combined with others. */ -#define PYLIBMC_FLAG_NONE 0 -#define PYLIBMC_FLAG_PICKLE (1 << 0) -#define PYLIBMC_FLAG_INTEGER (1 << 1) -#define PYLIBMC_FLAG_LONG (1 << 2) -/* Note: this is an addition! python-memcached doesn't handle bools. */ -#define PYLIBMC_FLAG_BOOL (1 << 4) -#define PYLIBMC_FLAG_TYPES (PYLIBMC_FLAG_PICKLE | PYLIBMC_FLAG_INTEGER | \ - PYLIBMC_FLAG_LONG | PYLIBMC_FLAG_BOOL) -/* Modifier flags */ -#define PYLIBMC_FLAG_ZLIB (1 << 3) +enum PylibMC_Flags { + PYLIBMC_FLAG_NONE = 0, + PYLIBMC_FLAG_PICKLE = (1 << 0), + PYLIBMC_FLAG_INTEGER = (1 << 1), + PYLIBMC_FLAG_LONG = (1 << 2), + PYLIBMC_FLAG_ZLIB = (1 << 3), + PYLIBMC_FLAG_TEXT = (1 << 4), +}; + +#define PYLIBMC_FLAG_TYPES (PYLIBMC_FLAG_PICKLE | PYLIBMC_FLAG_INTEGER | \ + PYLIBMC_FLAG_LONG | PYLIBMC_FLAG_TEXT) +/* }}} */ + +/* Behaviors that only affects pylibmc (i.e. not memached_set_behavior etc) */ +enum PylibMC_Behaviors { + PYLIBMC_BEHAVIOR_PICKLE_PROTOCOL = 0xcafe0000, +}; /* Python 3 stuff */ #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #endif -/* }}} */ typedef memcached_return (*_PylibMC_SetCommand)(memcached_st *, const char *, size_t, const char *, size_t, time_t, uint32_t); @@ -228,6 +234,7 @@ #if LIBMEMCACHED_VERSION_HEX >= 0x01000003 { MEMCACHED_BEHAVIOR_DEAD_TIMEOUT, "dead_timeout" }, #endif + { PYLIBMC_BEHAVIOR_PICKLE_PROTOCOL, "pickle_protocol" }, { 0, NULL } }; @@ -278,6 +285,7 @@ uint8_t sasl_set; uint8_t native_serialization; uint8_t native_deserialization; + int pickle_protocol; } PylibMC_Client; /* {{{ Prototypes */ @@ -315,9 +323,9 @@ memcached_return, const char *, Py_ssize_t); static PyObject *PylibMC_ErrFromMemcached(PylibMC_Client *, const char *, memcached_return); -static PyObject *_PylibMC_Unpickle(const char *, Py_ssize_t); -static PyObject *_PylibMC_Unpickle_Bytes(PyObject *); -static PyObject *_PylibMC_Pickle(PyObject *); +static PyObject *_PylibMC_Unpickle(PylibMC_Client *, const char *, Py_ssize_t); +static PyObject *_PylibMC_Unpickle_Bytes(PylibMC_Client *, PyObject *); +static PyObject *_PylibMC_Pickle(PylibMC_Client *, PyObject *); static int _key_normalized_obj(PyObject **); static int _key_normalized_str(char **, Py_ssize_t *); static int _PylibMC_serialize_user(PylibMC_Client *, PyObject *, PyObject **, uint32_t *); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/src/pylibmc/__main__.py new/pylibmc-1.6.0/src/pylibmc/__main__.py --- old/pylibmc-1.5.2/src/pylibmc/__main__.py 2015-06-17 13:21:50.000000000 +0200 +++ new/pylibmc-1.6.0/src/pylibmc/__main__.py 2018-11-09 18:42:08.000000000 +0100 @@ -20,14 +20,14 @@ def collect_servers(): try: in_addr = raw_input("Address [127.0.0.1]: ") - except: + except NameError: in_addr = input("Address [127.0.0.1]: ") if in_addr: while in_addr: yield in_addr try: in_addr = raw_input("Address [<stop>]: ") - except: + except NameError: in_addr = input("Address [<stop>]: ") else: yield "127.0.0.1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/src/pylibmc/autoconf.py new/pylibmc-1.6.0/src/pylibmc/autoconf.py --- old/pylibmc-1.5.2/src/pylibmc/autoconf.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylibmc-1.6.0/src/pylibmc/autoconf.py 2018-11-09 18:42:08.000000000 +0100 @@ -0,0 +1,62 @@ +"Autoconfiguration" + +from __future__ import unicode_literals +import pylibmc + +class UnsupportedAutoconfMethod(Exception): + pass + +class NoAutoconfFound(Exception): + pass + +def _elasticache_config_get(address, key): + import socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + host, port = address.split(':') + port = int(port) + sock.connect((host, port)) + sock.send(('config get %s\r\n' % (key,)).encode('ascii')) + state = 'wait-nl-header' + nbytes = 0 + buff = b'' + while True: + chunk = sock.recv(4096) + if not chunk: + raise RuntimeError('failed reading cluster config') + buff += chunk + if state.startswith('wait-nl-') and b'\r\n' not in buff: + continue + elif state == 'wait-nl-header': + line, buff = buff.split(b'\r\n', 1) + if line.lower() == b'error': + raise UnsupportedAutoconfMethod() + cmd, key, flags, nbytes = line.split() + flags, nbytes = int(flags), int(nbytes) + state = 'read-body' + elif state == 'ready-body': + if len(buff) < nbytes: + continue + config, buff = buff[:nbytes], buff[nbytes+2:] + state = 'wait-nl-end' + elif state == 'wait-nl-end': + break + else: + raise RuntimeError(state) + return config + +def _parse_elasticache_config(cfg): + ver, nodes = cfg.split(b'\n') + ver, nodes = int(ver), [n.decode('ascii').split('|') for n in nodes.split()] + # NOTE Should probably verify ver == 12, but why not try anyways + return ['%s:%s' % (addr or cname, port) for (cname, addr, port) in nodes] + +def elasticache(address='127.0.0.1:11211', config_key=b'cluster', + mc_key='AmazonElastiCache:cluster'): + try: + config = _elasticache_config_get(address, config_key) + except UnsupportedAutoconfMethod: + config = pylibmc.Client([address]).get(mc_key) + if config is None: + raise NoAutoconfFound + hosts = _parse_elasticache_config(config) + return pylibmc.Client(hosts) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/src/pylibmc-version.h new/pylibmc-1.6.0/src/pylibmc-version.h --- old/pylibmc-1.5.2/src/pylibmc-version.h 2017-03-06 14:10:05.000000000 +0100 +++ new/pylibmc-1.6.0/src/pylibmc-version.h 2018-11-09 19:05:41.000000000 +0100 @@ -1 +1 @@ -#define PYLIBMC_VERSION "1.5.2" +#define PYLIBMC_VERSION "1.6.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/tests/__init__.py new/pylibmc-1.6.0/tests/__init__.py --- old/pylibmc-1.5.2/tests/__init__.py 2016-02-25 17:51:25.000000000 +0100 +++ new/pylibmc-1.6.0/tests/__init__.py 2018-11-09 18:42:08.000000000 +0100 @@ -23,14 +23,6 @@ self.mc.disconnect_all() del self.mc -def dump_infos(): - if hasattr(_pylibmc, "__file__"): - print("Starting tests with _pylibmc at", _pylibmc.__file__) - else: - print("Starting tests with static _pylibmc:", _pylibmc) - print("Reported libmemcached version:", _pylibmc.libmemcached_version) - print("Reported pylibmc version:", _pylibmc.__version__) - print("Support compression:", _pylibmc.support_compression) def get_refcounts(refcountables): """Measure reference counts during testing. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/tests/test_autoconf.py new/pylibmc-1.6.0/tests/test_autoconf.py --- old/pylibmc-1.5.2/tests/test_autoconf.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pylibmc-1.6.0/tests/test_autoconf.py 2018-11-09 18:42:08.000000000 +0100 @@ -0,0 +1,21 @@ +from pylibmc import autoconf +from tests import PylibmcTestCase + +class AutoConfTests(PylibmcTestCase): + def test_no_autoconf(self): + self.mc.delete('AmazonElastiCache:cluster') + self.assertRaises(autoconf.NoAutoconfFound, autoconf.elasticache) + + def test_autoconf(self): + addrtup = (self.memcached_host, self.memcached_port) + self.mc.set('AmazonElastiCache:cluster', ('12\nlocalhost|%s|%s' % addrtup).encode('ascii')) + mc = autoconf.elasticache(address=('%s:%s' % addrtup)) + self.assert_(mc.set('a', 'b')) + self.assertEqual(mc.get('a'), 'b') + + def test_autoconf_only_cname(self): + addrtup = (self.memcached_host, self.memcached_port) + self.mc.set('AmazonElastiCache:cluster', ('12\n%s||%s' % addrtup).encode('ascii')) + mc = autoconf.elasticache(address=('%s:%s' % addrtup)) + self.assert_(mc.set('a', 'b')) + self.assertEqual(mc.get('a'), 'b') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/tests/test_client.py new/pylibmc-1.6.0/tests/test_client.py --- old/pylibmc-1.5.2/tests/test_client.py 2016-08-09 18:06:26.000000000 +0200 +++ new/pylibmc-1.6.0/tests/test_client.py 2018-11-09 18:42:08.000000000 +0100 @@ -43,9 +43,9 @@ expected_behaviors = [ 'auto_eject', 'buffer_requests', 'cas', 'connect_timeout', 'distribution', 'failure_limit', 'hash', 'ketama', 'ketama_hash', - 'ketama_weighted', 'no_block', 'num_replicas', 'receive_timeout', - 'retry_timeout', 'send_timeout', 'tcp_keepalive', 'tcp_nodelay', - 'verify_keys'] + 'ketama_weighted', 'no_block', 'num_replicas', 'pickle_protocol', + 'receive_timeout', 'retry_timeout', 'send_timeout', + 'tcp_keepalive', 'tcp_nodelay', 'verify_keys'] # Since some parts of pyblibmc's functionality depend on the # libmemcached version, programatically check for the expected values diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pylibmc-1.5.2/tests/test_serialization.py new/pylibmc-1.6.0/tests/test_serialization.py --- old/pylibmc-1.5.2/tests/test_serialization.py 2016-08-18 15:30:00.000000000 +0200 +++ new/pylibmc-1.6.0/tests/test_serialization.py 2018-11-09 18:42:08.000000000 +0100 @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from __future__ import unicode_literals from __future__ import print_function @@ -25,6 +27,8 @@ # this happens under Python 3 return val +f_none = 0 +f_pickle, f_int, f_long, f_zlib, f_text = (1 << i for i in range(5)) class SerializationMethodTests(PylibmcTestCase): """Coverage tests for serialize and deserialize.""" @@ -32,29 +36,33 @@ def test_integers(self): c = make_test_client(binary=True) if sys.version_info[0] == 3: - eq_(c.serialize(1), (b'1', 4)) - eq_(c.serialize(2**64), (b'18446744073709551616', 4)) + eq_(c.serialize(1), (b'1', f_long)) + eq_(c.serialize(2**64), (b'18446744073709551616', f_long)) else: - eq_(c.serialize(1), (b'1', 2)) - eq_(c.serialize(2**64), (b'18446744073709551616', 4)) + eq_(c.serialize(1), (b'1', f_int)) + eq_(c.serialize(2**64), (b'18446744073709551616', f_long)) - eq_(c.deserialize(b'1', 2), 1) + eq_(c.deserialize(b'1', f_int), 1) - eq_(c.deserialize(b'18446744073709551616', 4), 2**64) - eq_(c.deserialize(b'1', 4), long_(1)) + eq_(c.deserialize(b'18446744073709551616', f_long), 2**64) + eq_(c.deserialize(b'1', f_long), long_(1)) def test_nonintegers(self): # tuples (python_value, (expected_bytestring, expected_flags)) SERIALIZATION_TEST_VALUES = [ - # booleans - (True, (b'1', 16)), - (False, (b'0', 16)), + # booleans are just ints + (True, (b'1', f_int)), + (False, (b'0', f_int)), # bytestrings - (b'asdf', (b'asdf', 0)), - (b'\xb5\xb1\xbf\xed\xa9\xc2{8', (b'\xb5\xb1\xbf\xed\xa9\xc2{8', 0)), + (b'asdf', (b'asdf', f_none)), + (b'\xb5\xb1\xbf\xed\xa9\xc2{8', (b'\xb5\xb1\xbf\xed\xa9\xc2{8', f_none)), + (b'', (b'', f_none)), + # unicode objects + (u'åäö', (u'åäö'.encode('utf-8'), f_text)), + (u'', (b'', f_text)), # objects (datetime.date(2015, 12, 28), (pickle.dumps(datetime.date(2015, 12, 28), - protocol=-1), 1)), + protocol=-1), f_pickle)), ] c = make_test_client(binary=True)
