Hello community,

here is the log from the commit of package python-pymongo for openSUSE:Factory 
checked in at 2013-11-28 16:52:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pymongo (Old)
 and      /work/SRC/openSUSE:Factory/.python-pymongo.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pymongo"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pymongo/python-pymongo.changes    
2013-09-16 16:35:17.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-pymongo.new/python-pymongo.changes       
2013-11-28 16:52:28.000000000 +0100
@@ -1,0 +2,12 @@
+Wed Nov 27 13:25:30 UTC 2013 - [email protected]
+
+- Update to version 2.6.3
+  + fix : AttributeError raised when use_greenlets=True is specified
+    without gevent (https://jira.mongodb.org/browse/PYTHON-561)
+  + fix : Semaphore leak during connection failure. 
+    (https://jira.mongodb.org/browse/PYTHON-580)
+  + fix : MongoReplicaSetClient ignores waitQueueMultiple and 
+    waitQueueTimeoutMS 
+    (https://jira.mongodb.org/browse/PYTHON-579)
+
+-------------------------------------------------------------------

Old:
----
  pymongo-2.6.2.tar.gz

New:
----
  pymongo-2.6.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pymongo.spec ++++++
--- /var/tmp/diff_new_pack.jlkJji/_old  2013-11-28 16:52:29.000000000 +0100
+++ /var/tmp/diff_new_pack.jlkJji/_new  2013-11-28 16:52:29.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-pymongo
-Version:        2.6.2
+Version:        2.6.3
 Release:        0
 Url:            http://github.com/mongodb/mongo-python-driver
 Summary:        Python driver for MongoDB

++++++ pymongo-2.6.2.tar.gz -> pymongo-2.6.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/PKG-INFO new/pymongo-2.6.3/PKG-INFO
--- old/pymongo-2.6.2/PKG-INFO  2013-09-06 22:56:36.000000000 +0200
+++ new/pymongo-2.6.3/PKG-INFO  2013-10-11 19:22:15.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pymongo
-Version: 2.6.2
+Version: 2.6.3
 Summary: Python driver for MongoDB <http://www.mongodb.org>
 Home-page: http://github.com/mongodb/mongo-python-driver
 Author: Bernie Hackett
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/bson/__init__.py 
new/pymongo-2.6.3/bson/__init__.py
--- old/pymongo-2.6.2/bson/__init__.py  2013-08-19 19:17:17.000000000 +0200
+++ new/pymongo-2.6.3/bson/__init__.py  2013-10-10 21:58:27.000000000 +0200
@@ -139,13 +139,19 @@
 
 
 def _get_string(data, position, as_class, tz_aware, uuid_subtype):
-    length = struct.unpack("<i", data[position:position + 4])[0] - 1
+    length = struct.unpack("<i", data[position:position + 4])[0]
+    if (len(data) - position - 4) < length:
+        raise InvalidBSON("invalid string length")
     position += 4
-    return _get_c_string(data, position, length)
+    if data[position + length - 1:position + length] != ZERO:
+        raise InvalidBSON("invalid end of string")
+    return _get_c_string(data, position, length - 1)
 
 
 def _get_object(data, position, as_class, tz_aware, uuid_subtype):
     obj_size = struct.unpack("<i", data[position:position + 4])[0]
+    if data[position + obj_size - 1:position + obj_size] != ZERO:
+        raise InvalidBSON("bad eoo")
     encoded = data[position + 4:position + obj_size - 1]
     object = _elements_to_dict(encoded, as_class, tz_aware, uuid_subtype)
     position += obj_size
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/bson/_cbsonmodule.c 
new/pymongo-2.6.3/bson/_cbsonmodule.c
--- old/pymongo-2.6.2/bson/_cbsonmodule.c       2013-08-19 19:17:17.000000000 
+0200
+++ new/pymongo-2.6.3/bson/_cbsonmodule.c       2013-10-11 01:09:29.000000000 
+0200
@@ -105,6 +105,8 @@
 #define JAVA_LEGACY   5
 #define CSHARP_LEGACY 6
 #define BSON_MAX_SIZE 2147483647
+/* The smallest possible BSON document, i.e. "{}" */
+#define BSON_MIN_SIZE 5
 
 /* Get an error class from the bson.errors module.
  *
@@ -136,8 +138,9 @@
     return (int)size + extra;
 }
 
-static PyObject* elements_to_dict(PyObject* self, const char* string, int max,
-                                  PyObject* as_class, unsigned char tz_aware,
+static PyObject* elements_to_dict(PyObject* self, const char* string,
+                                  unsigned max, PyObject* as_class,
+                                  unsigned char tz_aware,
                                   unsigned char uuid_subtype);
 
 static int _write_element_to_buffer(PyObject* self, buffer_t buffer, int 
type_byte,
@@ -1331,8 +1334,8 @@
     return result;
 }
 
-static PyObject* get_value(PyObject* self, const char* buffer, int* position,
-                           int type, int max, PyObject* as_class,
+static PyObject* get_value(PyObject* self, const char* buffer, unsigned* 
position,
+                           int type, unsigned max, PyObject* as_class,
                            unsigned char tz_aware, unsigned char uuid_subtype) 
{
     struct module_state *state = GETSTATE(self);
 
@@ -1356,24 +1359,40 @@
     case 2:
     case 14:
         {
-            int value_length = ((int*)(buffer + *position))[0] - 1;
-            if (max < value_length) {
+            unsigned value_length;
+            if (max < 4) {
+                goto invalid;
+            }
+            memcpy(&value_length, buffer + *position, 4);
+            /* Encoded string length + string */
+            if (max < 4 + value_length) {
                 goto invalid;
             }
             *position += 4;
-            value = PyUnicode_DecodeUTF8(buffer + *position, value_length, 
"strict");
+            /* Strings must end in \0 */
+            if (buffer[*position + value_length - 1]) {
+                goto invalid;
+            }
+            value = PyUnicode_DecodeUTF8(buffer + *position, value_length - 1, 
"strict");
             if (!value) {
                 return NULL;
             }
-            *position += value_length + 1;
+            *position += value_length;
             break;
         }
     case 3:
         {
             PyObject* collection;
-            int size;
+            unsigned size;
+            if (max < 4) {
+                goto invalid;
+            }
             memcpy(&size, buffer + *position, 4);
-            if (size < 0 || max < size) {
+            if (size < BSON_MIN_SIZE || max < size) {
+                goto invalid;
+            }
+            /* Check for bad eoo */
+            if (buffer[*position + size - 1]) {
                 goto invalid;
             }
             value = elements_to_dict(self, buffer + *position + 4,
@@ -1427,14 +1446,20 @@
         }
     case 4:
         {
-            int size,
-                end;
+            unsigned size, end;
 
+            if (max < 4) {
+                goto invalid;
+            }
             memcpy(&size, buffer + *position, 4);
             if (max < size) {
                 goto invalid;
             }
             end = *position + size - 1;
+            /* Check for bad eoo */
+            if (buffer[end]) {
+                goto invalid;
+            }
             *position += 4;
 
             value = PyList_New(0);
@@ -1446,14 +1471,20 @@
 
                 int bson_type = (int)buffer[(*position)++];
                 size_t key_size = strlen(buffer + *position);
-                if (key_size > BSON_MAX_SIZE) {
+                if (max < key_size) {
                     Py_DECREF(value);
                     goto invalid;
                 }
                 /* just skip the key, they're in order. */
-                *position += (int)key_size + 1; 
+                *position += (unsigned)key_size + 1;
+                if (Py_EnterRecursiveCall(" while decoding a list value")) {
+                    Py_DECREF(value);
+                    return NULL;
+                }
                 to_append = get_value(self, buffer, position, bson_type,
-                                      max - (int)key_size, as_class, tz_aware, 
uuid_subtype);
+                                      max - (unsigned)key_size,
+                                      as_class, tz_aware, uuid_subtype);
+                Py_LeaveRecursiveCall();
                 if (!to_append) {
                     Py_DECREF(value);
                     return NULL;
@@ -1468,8 +1499,11 @@
         {
             PyObject* data;
             PyObject* st;
-            int length, subtype;
+            unsigned length, subtype;
 
+            if (max < 4) {
+                goto invalid;
+            }
             memcpy(&length, buffer + *position, 4);
             if (max < length) {
                 goto invalid;
@@ -1656,19 +1690,21 @@
             int flags;
             size_t flags_length, i;
             size_t pattern_length = strlen(buffer + *position);
-            if (pattern_length > BSON_MAX_SIZE || max < (int)pattern_length) {
+            if (pattern_length > BSON_MAX_SIZE || max < pattern_length) {
                 goto invalid;
             }
             pattern = PyUnicode_DecodeUTF8(buffer + *position, pattern_length, 
"strict");
             if (!pattern) {
                 return NULL;
             }
-            *position += (int)pattern_length + 1;
-            if ((flags_length = strlen(buffer + *position)) > BSON_MAX_SIZE) {
+            *position += (unsigned)pattern_length + 1;
+            flags_length = strlen(buffer + *position);
+            if (flags_length > BSON_MAX_SIZE ||
+                    (BSON_MAX_SIZE - pattern_length) < flags_length) {
                 Py_DECREF(pattern);
                 goto invalid;
             }
-            if (max < (int)(pattern_length + flags_length)) {
+            if (max < pattern_length + flags_length) {
                 Py_DECREF(pattern);
                 goto invalid;
             }
@@ -1688,28 +1724,37 @@
                     flags |= 64;
                 }
             }
-            *position += (int)flags_length + 1;
+            *position += (unsigned)flags_length + 1;
             value = PyObject_CallFunction(state->RECompile, "Oi", pattern, 
flags);
             Py_DECREF(pattern);
             break;
         }
     case 12:
         {
-            size_t coll_length;
+            unsigned coll_length;
             PyObject* collection;
             PyObject* id;
 
+            if (max < 4) {
+                goto invalid;
+            }
+            memcpy(&coll_length, buffer + *position, 4);
+            /* Encoded string length + string + 12 byte ObjectId */
+            if (max < 4 + coll_length + 12) {
+                goto invalid;
+            }
             *position += 4;
-            coll_length = strlen(buffer + *position);
-            if (coll_length > BSON_MAX_SIZE || max < (int)coll_length + 12) {
+            /* Strings must end in \0 */
+            if (buffer[*position + coll_length - 1]) {
                 goto invalid;
             }
+
             collection = PyUnicode_DecodeUTF8(buffer + *position,
-                                              coll_length, "strict");
+                                              coll_length - 1, "strict");
             if (!collection) {
                 return NULL;
             }
-            *position += (int)coll_length + 1;
+            *position += coll_length;
 
             id = PyObject_CallFunction(state->ObjectId, "s#", buffer + 
*position, 12);
             if (!id) {
@@ -1725,41 +1770,82 @@
     case 13:
         {
             PyObject* code;
-            int value_length = ((int*)(buffer + *position))[0] - 1;
-            if (max < value_length) {
+            unsigned value_length;
+            if (max < 4) {
+                goto invalid;
+            }
+            memcpy(&value_length, buffer + *position, 4);
+            /* Encoded string length + string */
+            if (max < 4 + value_length) {
                 goto invalid;
             }
             *position += 4;
-            code = PyUnicode_DecodeUTF8(buffer + *position, value_length, 
"strict");
+            /* Strings must end in \0 */
+            if (buffer[*position + value_length - 1]) {
+                goto invalid;
+            }
+            code = PyUnicode_DecodeUTF8(buffer + *position, value_length - 1, 
"strict");
             if (!code) {
                 return NULL;
             }
-            *position += value_length + 1;
+            *position += value_length;
             value = PyObject_CallFunctionObjArgs(state->Code, code, NULL, 
NULL);
             Py_DECREF(code);
             break;
         }
     case 15:
         {
-            size_t code_length;
-            int scope_size;
+            unsigned c_w_s_size;
+            unsigned code_size;
+            unsigned scope_size;
             PyObject* code;
             PyObject* scope;
 
-            *position += 8;
-            code_length = strlen(buffer + *position);
-            if (code_length > BSON_MAX_SIZE || max < 8 + (int)code_length) {
+            if (max < 8) {
+                goto invalid;
+            }
+
+            memcpy(&c_w_s_size, buffer + *position, 4);
+            *position += 4;
+
+            if (max < c_w_s_size) {
                 goto invalid;
             }
-            code = PyUnicode_DecodeUTF8(buffer + *position, code_length, 
"strict");
+
+            memcpy(&code_size, buffer + *position, 4);
+            /* code_w_scope length + code length + code + scope length */
+            if (max < 4 + 4 + code_size + 4) {
+                goto invalid;
+            }
+            *position += 4;
+            /* Strings must end in \0 */
+            if (buffer[*position + code_size - 1]) {
+                goto invalid;
+            }
+            code = PyUnicode_DecodeUTF8(buffer + *position, code_size - 1, 
"strict");
             if (!code) {
                 return NULL;
             }
-            *position += (int)code_length + 1;
+            *position += code_size;
 
             memcpy(&scope_size, buffer + *position, 4);
-            scope = elements_to_dict(self, buffer + *position + 4, scope_size 
- 5,
-                                     (PyObject*)&PyDict_Type, tz_aware, 
uuid_subtype);
+            if (scope_size < BSON_MIN_SIZE) {
+                Py_DECREF(code);
+                goto invalid;
+            }
+            /* code length + code + scope length + scope */
+            if ((4 + code_size + 4 + scope_size) != c_w_s_size) {
+                Py_DECREF(code);
+                goto invalid;
+            }
+
+            /* Check for bad eoo */
+            if (buffer[*position + scope_size - 1]) {
+                goto invalid;
+            }
+            scope = elements_to_dict(self, buffer + *position + 4,
+                                     scope_size - 5, (PyObject*)&PyDict_Type,
+                                     tz_aware, uuid_subtype);
             if (!scope) {
                 Py_DECREF(code);
                 return NULL;
@@ -1845,16 +1931,18 @@
 
     error = _error("InvalidBSON");
     if (error) {
-        PyErr_SetNone(error);
+        PyErr_SetString(error,
+                        "invalid length or type code");
         Py_DECREF(error);
     }
     return NULL;
 }
 
-static PyObject* elements_to_dict(PyObject* self, const char* string, int max,
-                                  PyObject* as_class, unsigned char tz_aware,
-                                  unsigned char uuid_subtype) {
-    int position = 0;
+static PyObject* _elements_to_dict(PyObject* self, const char* string,
+                                   unsigned max, PyObject* as_class,
+                                   unsigned char tz_aware,
+                                   unsigned char uuid_subtype) {
+    unsigned position = 0;
     PyObject* dict = PyObject_CallObject(as_class, NULL);
     if (!dict) {
         return NULL;
@@ -1864,7 +1952,7 @@
         PyObject* value;
         int type = (int)string[position++];
         size_t name_length = strlen(string + position);
-        if (name_length > BSON_MAX_SIZE || position + (int)name_length >= max) 
{
+        if (name_length > BSON_MAX_SIZE || position + name_length >= max) {
             PyObject* InvalidBSON = _error("InvalidBSON");
             if (InvalidBSON) {
                 PyErr_SetNone(InvalidBSON);
@@ -1878,7 +1966,7 @@
             Py_DECREF(dict);
             return NULL;
         }
-        position += (int)name_length + 1;
+        position += (unsigned)name_length + 1;
         value = get_value(self, string, &position, type,
                           max - position, as_class, tz_aware, uuid_subtype);
         if (!value) {
@@ -1894,6 +1982,19 @@
     return dict;
 }
 
+static PyObject* elements_to_dict(PyObject* self, const char* string,
+                                  unsigned max, PyObject* as_class,
+                                  unsigned char tz_aware,
+                                  unsigned char uuid_subtype) {
+    PyObject* result;
+    if (Py_EnterRecursiveCall(" while decoding a BSON document"))
+        return NULL;
+    result = _elements_to_dict(self, string, max,
+                               as_class, tz_aware, uuid_subtype);
+    Py_LeaveRecursiveCall();
+    return result;
+}
+
 static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
     int size;
     Py_ssize_t total_size;
@@ -1924,7 +2025,7 @@
 #else
     total_size = PyString_Size(bson);
 #endif
-    if (total_size < 5) {
+    if (total_size < BSON_MIN_SIZE) {
         PyObject* InvalidBSON = _error("InvalidBSON");
         if (InvalidBSON) {
             PyErr_SetString(InvalidBSON,
@@ -1944,7 +2045,7 @@
     }
 
     memcpy(&size, string, 4);
-    if (size < 0) {
+    if (size < BSON_MIN_SIZE) {
         PyObject* InvalidBSON = _error("InvalidBSON");
         if (InvalidBSON) {
             PyErr_SetString(InvalidBSON, "invalid message size");
@@ -1953,7 +2054,7 @@
         return NULL;
     }
 
-    if (total_size < size) {
+    if (total_size < size || total_size > BSON_MAX_SIZE) {
         PyObject* InvalidBSON = _error("InvalidBSON");
         if (InvalidBSON) {
             PyErr_SetString(InvalidBSON, "objsize too large");
@@ -1971,7 +2072,8 @@
         return NULL;
     }
 
-    dict = elements_to_dict(self, string + 4, size - 5, as_class, tz_aware, 
uuid_subtype);
+    dict = elements_to_dict(self, string + 4, (unsigned)size - 5,
+                            as_class, tz_aware, uuid_subtype);
     if (!dict) {
         return NULL;
     }
@@ -2029,7 +2131,7 @@
         return NULL;
 
     while (total_size > 0) {
-        if (total_size < 5) {
+        if (total_size < BSON_MIN_SIZE) {
             PyObject* InvalidBSON = _error("InvalidBSON");
             if (InvalidBSON) {
                 PyErr_SetString(InvalidBSON,
@@ -2041,7 +2143,7 @@
         }
 
         memcpy(&size, string, 4);
-        if (size < 0) {
+        if (size < BSON_MIN_SIZE) {
             PyObject* InvalidBSON = _error("InvalidBSON");
             if (InvalidBSON) {
                 PyErr_SetString(InvalidBSON, "invalid message size");
@@ -2071,7 +2173,7 @@
             return NULL;
         }
 
-        dict = elements_to_dict(self, string + 4, size - 5,
+        dict = elements_to_dict(self, string + 4, (unsigned)size - 5,
                                 as_class, tz_aware, uuid_subtype);
         if (!dict) {
             Py_DECREF(result);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/doc/changelog.rst 
new/pymongo-2.6.3/doc/changelog.rst
--- old/pymongo-2.6.2/doc/changelog.rst 2013-09-06 22:44:12.000000000 +0200
+++ new/pymongo-2.6.3/doc/changelog.rst 2013-10-11 01:09:29.000000000 +0200
@@ -1,6 +1,20 @@
 Changelog
 =========
 
+Changes in Version 2.6.3
+------------------------
+
+Version 2.6.3 fixes issues reported since the release of 2.6.2, most
+importantly a semaphore leak when a connection to the server fails.
+
+Issues Resolved
+...............
+
+See the `PyMongo 2.6.3 release notes in JIRA`_ for the list of resolved issues
+in this release.
+
+.. _PyMongo 2.6.3 release notes in JIRA: 
https://jira.mongodb.org/browse/PYTHON/fixforversion/13098
+
 Changes in Version 2.6.2
 ------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/pymongo/__init__.py 
new/pymongo-2.6.3/pymongo/__init__.py
--- old/pymongo-2.6.2/pymongo/__init__.py       2013-09-06 22:48:20.000000000 
+0200
+++ new/pymongo-2.6.3/pymongo/__init__.py       2013-10-11 19:09:39.000000000 
+0200
@@ -67,7 +67,7 @@
 ALL = 2
 """Profile all operations."""
 
-version_tuple = (2, 6, 2)
+version_tuple = (2, 6, 3)
 
 def get_version_string():
     if isinstance(version_tuple[-1], basestring):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/pymongo/mongo_replica_set_client.py 
new/pymongo-2.6.3/pymongo/mongo_replica_set_client.py
--- old/pymongo-2.6.2/pymongo/mongo_replica_set_client.py       2013-08-19 
19:17:17.000000000 +0200
+++ new/pymongo-2.6.3/pymongo/mongo_replica_set_client.py       2013-10-11 
01:09:29.000000000 +0200
@@ -552,6 +552,12 @@
             receive on a socket can take before timing out.
           - `connectTimeoutMS`: (integer) How long (in milliseconds) a
             connection can take to be opened before timing out.
+          - `waitQueueTimeoutMS`: (integer) How long (in milliseconds) a
+            thread will wait for a socket from the pool if the pool has no
+            free sockets. Defaults to ``None`` (no timeout).
+          - `waitQueueMultiple`: (integer) Multiplied by max_pool_size to give
+            the number of threads allowed to wait for a socket at one time.
+            Defaults to ``None`` (no waiters).
           - `auto_start_request`: If ``True``, each thread that accesses
             this :class:`MongoReplicaSetClient` has a socket allocated to it
             for the thread's lifetime, for each member of the set. For
@@ -694,6 +700,8 @@
 
         self.__net_timeout = self.__opts.get('sockettimeoutms')
         self.__conn_timeout = self.__opts.get('connecttimeoutms')
+        self.__wait_queue_timeout = self.__opts.get('waitqueuetimeoutms')
+        self.__wait_queue_multiple = self.__opts.get('waitqueuemultiple')
         self.__use_ssl = self.__opts.get('ssl', None)
         self.__ssl_keyfile = self.__opts.get('ssl_keyfile', None)
         self.__ssl_certfile = self.__opts.get('ssl_certfile', None)
@@ -1034,6 +1042,8 @@
             self.__net_timeout,
             self.__conn_timeout,
             self.__use_ssl,
+            wait_queue_timeout=self.__wait_queue_timeout,
+            wait_queue_multiple=self.__wait_queue_multiple,
             use_greenlets=self.__use_greenlets,
             ssl_keyfile=self.__ssl_keyfile,
             ssl_certfile=self.__ssl_certfile,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/pymongo/pool.py 
new/pymongo-2.6.3/pymongo/pool.py
--- old/pymongo-2.6.2/pymongo/pool.py   2013-09-06 21:40:22.000000000 +0200
+++ new/pymongo-2.6.3/pymongo/pool.py   2013-10-11 01:03:02.000000000 +0200
@@ -141,12 +141,6 @@
         # Can override for testing: 0 to always check, None to never check.
         self._check_interval_seconds = 1
 
-        if use_greenlets and not thread_util.have_gevent:
-            raise ConfigurationError(
-                "The Gevent module is not available. "
-                "Install the gevent package from PyPI."
-            )
-
         self.sockets = set()
         self.lock = threading.Lock()
 
@@ -169,11 +163,17 @@
         if HAS_SSL and use_ssl and not ssl_cert_reqs:
             self.ssl_cert_reqs = ssl.CERT_NONE
 
-        self._ident = thread_util.create_ident(use_greenlets)
-
         # Map self._ident.get() -> request socket
         self._tid_to_sock = {}
 
+        if use_greenlets and not thread_util.have_gevent:
+            raise ConfigurationError(
+                "The Gevent module is not available. "
+                "Install the gevent package from PyPI."
+            )
+
+        self._ident = thread_util.create_ident(use_greenlets)
+
         # Count the number of calls to start_request() per thread or greenlet
         self._request_counter = thread_util.Counter(use_greenlets)
 
@@ -324,28 +324,34 @@
         elif not self._socket_semaphore.acquire(True, self.wait_queue_timeout):
             self._raise_wait_queue_timeout()
 
-        sock_info, from_pool = None, None
+        # We've now acquired the semaphore and must release it on error.
         try:
+            sock_info, from_pool = None, None
             try:
-                # set.pop() isn't atomic in Jython less than 2.7, see
-                # http://bugs.jython.org/issue1854
-                self.lock.acquire()
-                sock_info, from_pool = self.sockets.pop(), True
-            finally:
-                self.lock.release()
-        except KeyError:
-            sock_info, from_pool = self.connect(pair), False
-
-        if from_pool:
-            sock_info = self._check(sock_info, pair)
-
-        sock_info.forced = forced
-
-        if req_state == NO_SOCKET_YET:
-            # start_request has been called but we haven't assigned a socket to
-            # the request yet. Let's use this socket for this request until
-            # end_request.
-            self._set_request_state(sock_info)
+                try:
+                    # set.pop() isn't atomic in Jython less than 2.7, see
+                    # http://bugs.jython.org/issue1854
+                    self.lock.acquire()
+                    sock_info, from_pool = self.sockets.pop(), True
+                finally:
+                    self.lock.release()
+            except KeyError:
+                sock_info, from_pool = self.connect(pair), False
+
+            if from_pool:
+                sock_info = self._check(sock_info, pair)
+
+            sock_info.forced = forced
+
+            if req_state == NO_SOCKET_YET:
+                # start_request has been called but we haven't assigned a
+                # socket to the request yet. Let's use this socket for this
+                # request until end_request.
+                self._set_request_state(sock_info)
+        except:
+            if not forced:
+                self._socket_semaphore.release()
+            raise
 
         sock_info.last_checkout = time.time()
         return sock_info
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/pymongo/replica_set_connection.py 
new/pymongo-2.6.3/pymongo/replica_set_connection.py
--- old/pymongo-2.6.2/pymongo/replica_set_connection.py 2013-08-16 
23:12:51.000000000 +0200
+++ new/pymongo-2.6.3/pymongo/replica_set_connection.py 2013-10-11 
01:09:29.000000000 +0200
@@ -110,6 +110,12 @@
             receive on a socket can take before timing out.
           - `connectTimeoutMS`: (integer) How long (in milliseconds) a
             connection can take to be opened before timing out.
+          - `waitQueueTimeoutMS`: (integer) How long (in milliseconds) a
+            thread will wait for a socket from the pool if the pool has no
+            free sockets. Defaults to ``None`` (no timeout).
+          - `waitQueueMultiple`: (integer) Multiplied by max_pool_size to give
+            the number of threads allowed to wait for a socket at one time.
+            Defaults to ``None`` (no waiters).
           - `auto_start_request`: If ``True`` (the default), each thread that
             accesses this :class:`ReplicaSetConnection` has a socket allocated
             to it for the thread's lifetime, for each member of the set. For
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/pymongo.egg-info/PKG-INFO 
new/pymongo-2.6.3/pymongo.egg-info/PKG-INFO
--- old/pymongo-2.6.2/pymongo.egg-info/PKG-INFO 2013-09-06 22:56:36.000000000 
+0200
+++ new/pymongo-2.6.3/pymongo.egg-info/PKG-INFO 2013-10-11 19:22:15.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pymongo
-Version: 2.6.2
+Version: 2.6.3
 Summary: Python driver for MongoDB <http://www.mongodb.org>
 Home-page: http://github.com/mongodb/mongo-python-driver
 Author: Bernie Hackett
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/setup.py new/pymongo-2.6.3/setup.py
--- old/pymongo-2.6.2/setup.py  2013-09-06 22:49:23.000000000 +0200
+++ new/pymongo-2.6.3/setup.py  2013-10-11 19:09:26.000000000 +0200
@@ -31,7 +31,7 @@
 from distutils.errors import DistutilsPlatformError, DistutilsExecError
 from distutils.core import Extension
 
-version = "2.6.2"
+version = "2.6.3"
 
 f = open("README.rst")
 try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_bson.py 
new/pymongo-2.6.3/test/test_bson.py
--- old/pymongo-2.6.2/test/test_bson.py 2013-09-06 21:59:51.000000000 +0200
+++ new/pymongo-2.6.3/test/test_bson.py 2013-10-11 01:09:29.000000000 +0200
@@ -67,6 +67,8 @@
         # the simplest valid BSON document
         self.assertTrue(is_valid(b("\x05\x00\x00\x00\x00")))
         self.assertTrue(is_valid(BSON(b("\x05\x00\x00\x00\x00"))))
+
+        # failure cases
         self.assertFalse(is_valid(b("\x04\x00\x00\x00\x00")))
         self.assertFalse(is_valid(b("\x05\x00\x00\x00\x01")))
         self.assertFalse(is_valid(b("\x05\x00\x00\x00")))
@@ -74,6 +76,17 @@
         
self.assertFalse(is_valid(b("\x07\x00\x00\x00\x02a\x00\x78\x56\x34\x12")))
         self.assertFalse(is_valid(b("\x09\x00\x00\x00\x10a\x00\x05\x00")))
         
self.assertFalse(is_valid(b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")))
+        self.assertFalse(is_valid(b("\x13\x00\x00\x00\x02foo\x00"
+                                    "\x04\x00\x00\x00bar\x00\x00")))
+        self.assertFalse(is_valid(b("\x18\x00\x00\x00\x03foo\x00\x0f\x00\x00"
+                                    
"\x00\x10bar\x00\xff\xff\xff\x7f\x00\x00")))
+        self.assertFalse(is_valid(b("\x15\x00\x00\x00\x03foo\x00\x0c"
+                                    "\x00\x00\x00\x08bar\x00\x01\x00\x00")))
+        self.assertFalse(is_valid(b("\x1c\x00\x00\x00\x03foo\x00"
+                                    "\x12\x00\x00\x00\x02bar\x00"
+                                    "\x05\x00\x00\x00baz\x00\x00\x00")))
+        self.assertFalse(is_valid(b("\x10\x00\x00\x00\x02a\x00"
+                                    "\x04\x00\x00\x00abc\xff\x00")))
 
     def test_random_data_is_not_bson(self):
         qcheck.check_unittest(self, qcheck.isnt(is_valid),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_legacy_connections.py 
new/pymongo-2.6.3/test/test_legacy_connections.py
--- old/pymongo-2.6.2/test/test_legacy_connections.py   2013-09-06 
21:40:22.000000000 +0200
+++ new/pymongo-2.6.3/test/test_legacy_connections.py   2013-10-10 
21:58:26.000000000 +0200
@@ -28,6 +28,7 @@
 from pymongo.errors import ConfigurationError
 from test import host, port, pair
 from test.test_replica_set_client import TestReplicaSetClientBase
+from test.utils import get_pool
 
 
 class TestConnection(unittest.TestCase):
@@ -92,8 +93,7 @@
 
         # To preserve legacy ReplicaSetConnection's behavior, max_size should
         # be None. Pool should handle this without error.
-        rs_state = c._MongoReplicaSetClient__rs_state
-        pool = rs_state.primary_member.pool
+        pool = get_pool(c)
         self.assertEqual(None, pool.max_size)
         c.end_request()
 
@@ -104,7 +104,8 @@
         )._MongoReplicaSetClient__net_timeout)
 
         for network_timeout in 'foo', 0, -1:
-            self.assertRaises(ConfigurationError,
+            self.assertRaises(
+                ConfigurationError,
                 ReplicaSetConnection, pair, replicaSet=self.name,
                 network_timeout=network_timeout)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_pooling_base.py 
new/pymongo-2.6.3/test/test_pooling_base.py
--- old/pymongo-2.6.2/test/test_pooling_base.py 2013-09-06 21:59:51.000000000 
+0200
+++ new/pymongo-2.6.3/test/test_pooling_base.py 2013-10-11 01:09:55.000000000 
+0200
@@ -998,6 +998,28 @@
         # Call end_request() but not start_request()
         self._test_max_pool_size(0, 1)
 
+    def test_max_pool_size_with_connection_failure(self):
+        # The pool acquires its semaphore before attempting to connect; ensure
+        # it releases the semaphore on connection failure.
+        class TestPool(Pool):
+            def connect(self, pair):
+                raise socket.error()
+
+        test_pool = TestPool(
+            pair=('example.com', 27017),
+            max_size=1,
+            net_timeout=1,
+            conn_timeout=1,
+            use_ssl=False,
+            wait_queue_timeout=1,
+            use_greenlets=self.use_greenlets)
+
+        # First call to get_socket fails; if pool doesn't release its semaphore
+        # then the second call raises "ConnectionFailure: Timed out waiting for
+        # socket from pool" instead of the socket.error.
+        for i in range(2):
+            self.assertRaises(socket.error, test_pool.get_socket)
+
 
 class SocketGetter(MongoThread):
     """Utility for _TestMaxOpenSockets and _TestWaitQueueMultiple"""
@@ -1018,7 +1040,7 @@
     To be run both with threads and with greenlets.
     """
     def get_pool_with_wait_queue_timeout(self, wait_queue_timeout):
-        return self.get_pool(('127.0.0.1', 27017),
+        return self.get_pool((host, port),
                              1, None, None,
                              False,
                              wait_queue_timeout=wait_queue_timeout,
@@ -1066,7 +1088,7 @@
     To be run both with threads and with greenlets.
     """
     def get_pool_with_wait_queue_multiple(self, wait_queue_multiple):
-        return self.get_pool(('127.0.0.1', 27017),
+        return self.get_pool((host, port),
                              2, None, None,
                              False,
                              wait_queue_timeout=None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_pooling_gevent.py 
new/pymongo-2.6.3/test/test_pooling_gevent.py
--- old/pymongo-2.6.2/test/test_pooling_gevent.py       2013-06-14 
23:51:54.000000000 +0200
+++ new/pymongo-2.6.3/test/test_pooling_gevent.py       2013-10-11 
01:03:02.000000000 +0200
@@ -14,16 +14,19 @@
 
 """Tests for connection-pooling with greenlets and Gevent"""
 
+import gc
+import time
 import unittest
 
 from nose.plugins.skip import SkipTest
 
 from pymongo import pool
+from pymongo.errors import ConfigurationError
 from test import host, port
 from test.utils import looplet
 from test.test_pooling_base import (
     _TestPooling, _TestMaxPoolSize, _TestMaxOpenSockets,
-    _TestPoolSocketSharing, _TestWaitQueueMultiple)
+    _TestPoolSocketSharing, _TestWaitQueueMultiple, has_gevent)
 
 
 class TestPoolingGevent(_TestPooling, unittest.TestCase):
@@ -189,5 +192,49 @@
     use_greenlets = True
 
 
+class TestUseGreenletsWithoutGevent(unittest.TestCase):
+    def test_use_greenlets_without_gevent(self):
+        # Verify that Pool(use_greenlets=True) raises ConfigurationError if
+        # Gevent is not installed, and that its destructor runs without error.
+        if has_gevent:
+            raise SkipTest(
+                "Gevent is installed, can't test what happens calling "
+                "Pool(use_greenlets=True) when Gevent is unavailable")
+
+        # Possible outcomes of __del__.
+        DID_NOT_RUN, RAISED, SUCCESS = range(3)
+        outcome = [DID_NOT_RUN]
+
+        class TestPool(pool.Pool):
+            def __del__(self):
+                try:
+                    pool.Pool.__del__(self)  # Pool is old-style, no super()
+                    outcome[0] = SUCCESS
+                except:
+                    outcome[0] = RAISED
+
+        # Pool raises ConfigurationError, "The Gevent module is not available".
+        self.assertRaises(
+            ConfigurationError,
+            TestPool,
+            pair=(host, port),
+            max_size=10,
+            net_timeout=1000,
+            conn_timeout=1000,
+            use_ssl=False,
+            use_greenlets=True)
+
+        # Convince Jython or PyPy to call __del__.
+        for _ in range(10):
+            if outcome[0] == DID_NOT_RUN:
+                gc.collect()
+                time.sleep(0.1)
+
+        if outcome[0] == DID_NOT_RUN:
+            self.fail("Pool.__del__ didn't run")
+        elif outcome[0] == RAISED:
+            self.fail("Pool.__del__ raised exception")
+
+
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_replica_set_client.py 
new/pymongo-2.6.3/test/test_replica_set_client.py
--- old/pymongo-2.6.2/test/test_replica_set_client.py   2013-08-15 
20:33:49.000000000 +0200
+++ new/pymongo-2.6.3/test/test_replica_set_client.py   2013-10-11 
01:09:29.000000000 +0200
@@ -49,7 +49,8 @@
 from test import version, port, pair
 from test.utils import (
     delay, assertReadFrom, assertReadFromAll, read_from_which_host,
-    assertRaisesExactly, TestRequestMixin, one, server_started_with_auth)
+    assertRaisesExactly, TestRequestMixin, one, server_started_with_auth,
+    pools_from_rs_client, get_pool)
 
 
 class TestReplicaSetClientAgainstStandalone(unittest.TestCase):
@@ -699,8 +700,8 @@
         previous_writer = c._MongoReplicaSetClient__rs_state.writer
 
         def kill_sockets():
-            for member in c._MongoReplicaSetClient__rs_state.members:
-                for socket_info in member.pool.sockets:
+            for pool in pools_from_rs_client(c):
+                for socket_info in pool.sockets:
                     socket_info.sock.close()
 
         kill_sockets()
@@ -762,6 +763,17 @@
         self.assertTrue(rs_state.get(secondary_host).up)
         collection.find_one(read_preference=SECONDARY)  # No error.
 
+    def test_waitQueueTimeoutMS(self):
+        client = self._get_client(waitQueueTimeoutMS=2000)
+        pool = get_pool(client)
+        self.assertEqual(pool.wait_queue_timeout, 2)
+
+    def test_waitQueueMultiple(self):
+        client = self._get_client(max_pool_size=3, waitQueueMultiple=2)
+        pool = get_pool(client)
+        self.assertEqual(pool.wait_queue_multiple, 2)
+        self.assertEqual(pool._socket_semaphore.waiter_semaphore.counter, 6)
+
     def test_tz_aware(self):
         self.assertRaises(ConfigurationError, MongoReplicaSetClient,
                           tz_aware='foo', replicaSet=self.name)
@@ -923,7 +935,7 @@
         # Ensure MongoReplicaSetClient doesn't close socket after it gets an
         # error response to getLastError. PYTHON-395.
         c = self._get_client(auto_start_request=False)
-        pool = c._MongoReplicaSetClient__rs_state.get(self.primary).pool
+        pool = get_pool(c)
         self.assertEqual(1, len(pool.sockets))
         old_sock_info = iter(pool.sockets).next()
         c.pymongo_test.test.drop()
@@ -943,7 +955,7 @@
         # error response to getLastError. PYTHON-395.
         c = self._get_client(auto_start_request=True)
         c.pymongo_test.test.find_one()
-        pool = c._MongoReplicaSetClient__rs_state.get(self.primary).pool
+        pool = get_pool(c)
 
         # Client reserved a socket for this thread
         self.assertTrue(isinstance(pool._get_request_state(), SocketInfo))
@@ -968,13 +980,10 @@
 
         client = self._get_client(auto_start_request=True)
         self.assertTrue(client.auto_start_request)
-        pools = [member.pool for member in
-                 client._MongoReplicaSetClient__rs_state.members]
-
+        pools = pools_from_rs_client(client)
         self.assertInRequestAndSameSock(client, pools)
 
-        primary_pool = \
-            client._MongoReplicaSetClient__rs_state.get(client.primary).pool
+        primary_pool = get_pool(client)
 
         # Trigger the RSC to actually start a request on primary pool
         client.pymongo_test.test.find_one()
@@ -1003,9 +1012,7 @@
         client.close()
 
         client = self._get_client()
-        pools = [mongo.pool for mongo in
-                 client._MongoReplicaSetClient__rs_state.members]
-
+        pools = pools_from_rs_client(client)
         self.assertNotInRequestAndDifferentSock(client, pools)
         client.start_request()
         self.assertInRequestAndSameSock(client, pools)
@@ -1016,8 +1023,7 @@
     def test_nested_request(self):
         client = self._get_client(auto_start_request=True)
         try:
-            pools = [member.pool for member in
-                     client._MongoReplicaSetClient__rs_state.members]
+            pools = pools_from_rs_client(client)
             self.assertTrue(client.in_request())
 
             # Start and end request - we're still in "outer" original request
@@ -1059,8 +1065,7 @@
     def test_request_threads(self):
         client = self._get_client()
         try:
-            pools = [member.pool for member in
-                     client._MongoReplicaSetClient__rs_state.members]
+            pools = pools_from_rs_client(client)
             self.assertNotInRequestAndDifferentSock(client, pools)
 
             started_request, ended_request = threading.Event(), 
threading.Event()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/test_threads.py 
new/pymongo-2.6.3/test/test_threads.py
--- old/pymongo-2.6.2/test/test_threads.py      2013-06-04 00:49:43.000000000 
+0200
+++ new/pymongo-2.6.3/test/test_threads.py      2013-10-11 01:09:29.000000000 
+0200
@@ -22,22 +22,11 @@
 
 from test.utils import server_started_with_auth, joinall, RendezvousThread
 from test.test_client import get_client
-from pymongo.mongo_client import MongoClient
-from pymongo.replica_set_connection import MongoReplicaSetClient
+from test.utils import get_pool
 from pymongo.pool import SocketInfo, _closed
 from pymongo.errors import AutoReconnect, OperationFailure
 
 
-def get_pool(client):
-    if isinstance(client, MongoClient):
-        return client._MongoClient__pool
-    elif isinstance(client, MongoReplicaSetClient):
-        rs_state = client._MongoReplicaSetClient__rs_state
-        return rs_state[rs_state.writer].pool
-    else:
-        raise TypeError(str(client))
-
-
 class AutoAuthenticateThreads(threading.Thread):
 
     def __init__(self, collection, num):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-2.6.2/test/utils.py 
new/pymongo-2.6.3/test/utils.py
--- old/pymongo-2.6.2/test/utils.py     2013-07-09 00:51:44.000000000 +0200
+++ new/pymongo-2.6.3/test/utils.py     2013-10-11 01:09:29.000000000 +0200
@@ -263,6 +263,22 @@
 
     testcase.assertEqual(members, used)
 
+def get_pool(client):
+    if isinstance(client, MongoClient):
+        return client._MongoClient__pool
+    elif isinstance(client, MongoReplicaSetClient):
+        rs_state = client._MongoReplicaSetClient__rs_state
+        return rs_state.primary_member.pool
+    else:
+        raise TypeError(str(client))
+
+def pools_from_rs_client(client):
+    """Get Pool instances from a MongoReplicaSetClient or ReplicaSetConnection.
+    """
+    return [
+        member.pool for member in
+        client._MongoReplicaSetClient__rs_state.members]
+
 class TestRequestMixin(object):
     """Inherit from this class and from unittest.TestCase to get some
     convenient methods for testing connection pools and requests

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to