Hello community,

here is the log from the commit of package python3-pymongo for openSUSE:Factory 
checked in at 2015-05-25 11:16:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python3-pymongo (Old)
 and      /work/SRC/openSUSE:Factory/.python3-pymongo.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python3-pymongo"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python3-pymongo/python3-pymongo.changes  
2015-04-25 11:26:29.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python3-pymongo.new/python3-pymongo.changes     
2015-05-25 11:16:52.000000000 +0200
@@ -1,0 +2,9 @@
+Mon May 25 06:23:05 UTC 2015 - [email protected]
+
+- update to version 3.0.2:
+  * Auth can fail while connecting to replset with recovering members
+  * ReadPreference.NEAREST can route operations to arbiters in PyMongo
+    3.0
+  * ReadPreference instances aren't copyable
+
+-------------------------------------------------------------------

Old:
----
  pymongo-3.0.1.tar.gz

New:
----
  pymongo-3.0.2.tar.gz

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

Other differences:
------------------
++++++ python3-pymongo.spec ++++++
--- /var/tmp/diff_new_pack.swlpUi/_old  2015-05-25 11:16:53.000000000 +0200
+++ /var/tmp/diff_new_pack.swlpUi/_new  2015-05-25 11:16:53.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python3-pymongo
-Version:        3.0.1
+Version:        3.0.2
 Release:        0
 Url:            http://github.com/mongodb/mongo-python-driver
 Summary:        Python driver for MongoDB

++++++ pymongo-3.0.1.tar.gz -> pymongo-3.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/PKG-INFO new/pymongo-3.0.2/PKG-INFO
--- old/pymongo-3.0.1/PKG-INFO  2015-04-21 22:37:57.000000000 +0200
+++ new/pymongo-3.0.2/PKG-INFO  2015-05-12 21:28:05.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pymongo
-Version: 3.0.1
+Version: 3.0.2
 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-3.0.1/doc/api/bson/index.rst 
new/pymongo-3.0.2/doc/api/bson/index.rst
--- old/pymongo-3.0.1/doc/api/bson/index.rst    2015-04-14 20:25:10.000000000 
+0200
+++ new/pymongo-3.0.2/doc/api/bson/index.rst    2015-05-12 00:51:23.000000000 
+0200
@@ -16,6 +16,7 @@
    codec_options
    dbref
    errors
+   int64
    json_util
    max_key
    min_key
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/doc/api/bson/int64.rst 
new/pymongo-3.0.2/doc/api/bson/int64.rst
--- old/pymongo-3.0.1/doc/api/bson/int64.rst    1970-01-01 01:00:00.000000000 
+0100
+++ new/pymongo-3.0.2/doc/api/bson/int64.rst    2015-05-12 00:51:23.000000000 
+0200
@@ -0,0 +1,7 @@
+:mod:`int64` -- Tools for representing BSON int64
+=================================================
+.. versionadded:: 3.0
+
+.. automodule:: bson.int64
+   :synopsis: Tools for representing BSON int64
+   :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/doc/changelog.rst 
new/pymongo-3.0.2/doc/changelog.rst
--- old/pymongo-3.0.1/doc/changelog.rst 2015-04-21 01:22:42.000000000 +0200
+++ new/pymongo-3.0.2/doc/changelog.rst 2015-05-12 01:11:51.000000000 +0200
@@ -1,6 +1,24 @@
 Changelog
 =========
 
+Changes in Version 3.0.2
+------------------------
+
+Version 3.0.2 fixes issues reported since the release of 3.0.1, most
+importantly a bug that could route operations to replica set members
+that are not in primary or secondary state when using
+:class:`~pymongo.read_preferences.PrimaryPreferred` or
+:class:`~pymongo.read_preferences.Nearest`. It is a recommended upgrade for
+all users of PyMongo 3.0.x.
+
+Issues Resolved
+...............
+
+See the `PyMongo 3.0.2 release notes in JIRA`_ for the list of resolved issues
+in this release.
+
+.. _PyMongo 3.0.2 release notes in JIRA: 
https://jira.mongodb.org/browse/PYTHON/fixforversion/15430
+
 Changes in Version 3.0.1
 ------------------------
 
@@ -424,6 +442,12 @@
 attempt to convert from a BSON regular expression to a Python regular
 expression object.
 
+PyMongo now decodes the int64 BSON type to :class:`~bson.int64.Int64`, a
+trivial wrapper around long (in python 2.x) or int (in python 3.x). This
+allows BSON int64 to be round tripped without losing type information in
+python 3. Note that if you store a python long (or a python int larger than
+4 bytes) it will be returned from PyMongo as :class:`~bson.int64.Int64`.
+
 The `as_class`, `tz_aware`, and `uuid_subtype` options are removed from all
 BSON encoding and decoding methods. Use
 :class:`~bson.codec_options.CodecOptions` to configure these options. The
@@ -450,6 +474,20 @@
 
 .. _PyMongo 3.0 release notes in JIRA: 
https://jira.mongodb.org/browse/PYTHON/fixforversion/12501
 
+Changes in Version 2.8.1
+------------------------
+
+Version 2.8.1 fixes a number of issues reported since the release of PyMongo
+2.8. It is a recommended upgrade for all users of PyMongo 2.x.
+
+Issues Resolved
+...............
+
+See the `PyMongo 2.8.1 release notes in JIRA`_ for the list of resolved issues
+in this release.
+
+.. _PyMongo 2.8.1 release notes in JIRA: 
https://jira.mongodb.org/browse/PYTHON/fixforversion/15324
+
 Changes in Version 2.8
 ----------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/doc/tools.rst 
new/pymongo-3.0.2/doc/tools.rst
--- old/pymongo-3.0.1/doc/tools.rst     2015-04-02 00:41:30.000000000 +0200
+++ new/pymongo-3.0.2/doc/tools.rst     2015-05-08 01:13:35.000000000 +0200
@@ -107,6 +107,9 @@
   project to enable using MongoDB as a backend for `beaker's
   <http://beaker.groovie.org/>`_ caching / session system.
   `The source is on github <http://github.com/bwmcadams/mongodb_beaker>`_.
+* `Log4Mongo <https://github.com/log4mongo/log4mongo-python>`_ is a flexible
+  Python logging handler that can store logs in MongoDB using normal and capped
+  collections.
 * `MongoLog <http://github.com/puentesarrin/mongodb-log/>`_ is a Python logging
   handler that stores logs in MongoDB using a capped collection.
 * `c5t <http://bitbucket.org/percious/c5t/>`_ is a content-management system
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/__init__.py 
new/pymongo-3.0.2/pymongo/__init__.py
--- old/pymongo-3.0.1/pymongo/__init__.py       2015-04-21 22:28:15.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo/__init__.py       2015-05-12 21:21:52.000000000 
+0200
@@ -70,7 +70,7 @@
 ALL = 2
 """Profile all operations."""
 
-version_tuple = (3, 0, 1)
+version_tuple = (3, 0, 2)
 
 def get_version_string():
     if isinstance(version_tuple[-1], str):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/command_cursor.py 
new/pymongo-3.0.2/pymongo/command_cursor.py
--- old/pymongo-3.0.1/pymongo/command_cursor.py 2015-04-20 22:03:23.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo/command_cursor.py 2015-05-12 00:51:23.000000000 
+0200
@@ -147,11 +147,16 @@
     def alive(self):
         """Does this cursor have the potential to return more data?
 
-        Even if :attr:`alive` is ``True``, :meth:`.next` can raise
+        Even if :attr:`alive` is ``True``, :meth:`next` can raise
         :exc:`StopIteration`. Best to use a for loop::
 
             for doc in collection.aggregate(pipeline):
                 print(doc)
+
+        .. note:: :attr:`alive` can be True while iterating a cursor from
+          a failed server. In this case :attr:`alive` will return False after
+          :meth:`next` fails to retrieve the next batch of results from the
+          server.
         """
         return bool(len(self.__data) or (not self.__killed))
 
@@ -172,8 +177,7 @@
         return self
 
     def next(self):
-        """Advance the cursor.
-        """
+        """Advance the cursor."""
         if len(self.__data) or self._refresh():
             coll = self.__collection
             return coll.database._fix_outgoing(self.__data.popleft(), coll)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/common.py 
new/pymongo-3.0.2/pymongo/common.py
--- old/pymongo-3.0.1/pymongo/common.py 2015-04-14 20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/common.py 2015-05-12 00:51:23.000000000 +0200
@@ -220,6 +220,15 @@
     return value
 
 
+def validate_positive_float_or_zero(option, value):
+    """Validates that 'value' is 0 or a positive float, or can be converted to
+    0 or a positive float.
+    """
+    if value == 0 or value == "0":
+        return 0
+    return validate_positive_float(option, value)
+
+
 def validate_timeout_or_none(option, value):
     """Validates a timeout specified in milliseconds returning
     a value in floating point seconds.
@@ -393,7 +402,7 @@
     'read_preference': validate_read_preference,
     'readpreference': validate_read_preference_mode,
     'readpreferencetags': validate_read_preference_tags,
-    'localthresholdms': validate_positive_float,
+    'localthresholdms': validate_positive_float_or_zero,
     'serverselectiontimeoutms': validate_timeout_or_zero,
     'authmechanism': validate_auth_mechanism,
     'authsource': validate_string,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/cursor.py 
new/pymongo-3.0.2/pymongo/cursor.py
--- old/pymongo-3.0.1/pymongo/cursor.py 2015-04-20 22:03:23.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/cursor.py 2015-05-12 00:51:23.000000000 +0200
@@ -942,6 +942,12 @@
 
             for doc in collection.find():
                 print(doc)
+
+        .. note:: Even if :attr:`alive` is True, :meth:`next` can raise
+          :exc:`StopIteration`. :attr:`alive` can also be True while iterating
+          a cursor from a failed server. In this case :attr:`alive` will
+          return False after :meth:`next` fails to retrieve the next batch
+          of results from the server.
         """
         return bool(len(self.__data) or (not self.__killed))
 
@@ -969,7 +975,8 @@
     def __iter__(self):
         return self
 
-    def __next__(self):
+    def next(self):
+        """Advance the cursor."""
         if self.__empty:
             raise StopIteration
         _db = self.__collection.database
@@ -982,7 +989,7 @@
         else:
             raise StopIteration
 
-    next = __next__
+    __next__ = next
 
     def __enter__(self):
         return self
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/ismaster.py 
new/pymongo-3.0.2/pymongo/ismaster.py
--- old/pymongo-3.0.1/pymongo/ismaster.py       2015-04-14 20:25:10.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo/ismaster.py       2015-05-12 00:51:23.000000000 
+0200
@@ -112,6 +112,10 @@
         return self._doc.get('maxWireVersion', common.MAX_WIRE_VERSION)
 
     @property
+    def election_id(self):
+        return self._doc.get('electionId')
+
+    @property
     def is_writable(self):
         return self._is_writable
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/mongo_client.py 
new/pymongo-3.0.2/pymongo/mongo_client.py
--- old/pymongo-3.0.1/pymongo/mongo_client.py   2015-04-21 21:56:43.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo/mongo_client.py   2015-05-12 00:51:23.000000000 
+0200
@@ -555,10 +555,14 @@
 
     @property
     def nodes(self):
-        """List of all connected servers.
+        """Set of all currently connected servers.
 
-        Nodes are either specified when this instance was created,
-        or discovered through the replica set discovery mechanism.
+        .. warning:: When connected to a replica set the value of :attr:`nodes`
+          can change over time as :class:`MongoClient`'s view of the replica
+          set changes. :attr:`nodes` can also be an empty set when
+          :class:`MongoClient` is first instantiated and hasn't yet connected
+          to any servers, or a network partition causes it to lose connection
+          to all servers.
         """
         description = self._topology.description
         return frozenset(s.address for s in description.known_servers)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/read_preferences.py 
new/pymongo-3.0.2/pymongo/read_preferences.py
--- old/pymongo-3.0.1/pymongo/read_preferences.py       2015-04-14 
20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/read_preferences.py       2015-05-12 
00:51:23.000000000 +0200
@@ -124,6 +124,19 @@
     def __ne__(self, other):
         return not self == other
 
+    def __getstate__(self):
+        """Return value of object for pickling.
+
+        Needed explicitly because __slots__() defined.
+        """
+        return {'mode': self.__mode, 'tag_sets': self.__tag_sets}
+
+    def __setstate__(self, value):
+        """Restore from pickling."""
+        self.__mode = value['mode']
+        self.__mongos_mode = _MONGOS_MODES[self.__mode]
+        self.__tag_sets = _validate_tag_sets(value['tag_sets'])
+
 
 class Primary(_ServerMode):
     """Primary read preference.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/server_description.py 
new/pymongo-3.0.2/pymongo/server_description.py
--- old/pymongo-3.0.1/pymongo/server_description.py     2015-04-14 
20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/server_description.py     2015-05-12 
00:51:23.000000000 +0200
@@ -32,7 +32,8 @@
         '_address', '_server_type', '_all_hosts', '_tags', '_replica_set_name',
         '_primary', '_max_bson_size', '_max_message_size',
         '_max_write_batch_size', '_min_wire_version', '_max_wire_version',
-        '_round_trip_time', '_is_writable', '_is_readable', '_error')
+        '_round_trip_time', '_is_writable', '_is_readable', '_error',
+        '_election_id')
 
     def __init__(
             self,
@@ -54,6 +55,7 @@
         self._max_write_batch_size = ismaster.max_write_batch_size
         self._min_wire_version = ismaster.min_wire_version
         self._max_wire_version = ismaster.max_wire_version
+        self._election_id = ismaster.election_id
         self._is_writable = ismaster.is_writable
         self._is_readable = ismaster.is_readable
         self._round_trip_time = round_trip_time
@@ -107,6 +109,10 @@
         return self._max_wire_version
 
     @property
+    def election_id(self):
+        return self._election_id
+
+    @property
     def round_trip_time(self):
         """The current average latency or None."""
         # This override is for unittesting only!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/server_selectors.py 
new/pymongo-3.0.2/pymongo/server_selectors.py
--- old/pymongo-3.0.1/pymongo/server_selectors.py       2015-04-20 
22:03:23.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/server_selectors.py       2015-05-12 
00:51:23.000000000 +0200
@@ -21,6 +21,10 @@
     return server_descriptions
 
 
+def readable_server_selector(server_descriptions):
+    return [s for s in server_descriptions if s.is_readable]
+
+
 def writable_server_selector(server_descriptions):
     return [s for s in server_descriptions if s.is_writable]
 
@@ -37,7 +41,9 @@
 
 def writable_preferred_server_selector(server_descriptions):
     """Like PrimaryPreferred but doesn't use tags or latency."""
-    return writable_server_selector(server_descriptions) or server_descriptions
+    return (
+        writable_server_selector(server_descriptions) or
+        secondary_server_selector(server_descriptions))
 
 
 def single_tag_set_server_selector(tag_set, server_descriptions):
@@ -47,6 +53,11 @@
     A server tagged {'a': '1', 'b': '2'} matches the tag set {'a': '1'}.
 
     The empty tag set {} matches any server.
+
+    The `server_descriptions` passed to this function should have
+    non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
+    out (e.g. by readable_server_selector or secondary_server_selector)
+    first.
     """
     def tags_match(server_tags):
         for key, value in tag_set.items():
@@ -66,6 +77,11 @@
     [{'a': 'value'}, {}] expresses a preference for servers tagged
     {'a': 'value'}, but accepts any server if none matches the first
     preference.
+
+    The `server_descriptions` passed to this function should have
+    non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
+    out (e.g. by readable_server_selector or secondary_server_selector)
+    first.
     """
     for tag_set in tag_sets:
         selected = single_tag_set_server_selector(tag_set, server_descriptions)
@@ -79,6 +95,11 @@
     """All servers with round trip times within latency_ms of the fastest one.
 
     No ServerDescription's round_trip_time can be None.
+
+    The `server_descriptions` passed to this function should have
+    non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
+    out (e.g. by readable_server_selector or secondary_server_selector)
+    first.
     """
     if not server_descriptions:
         # Avoid ValueError from min() with empty sequence.
@@ -91,7 +112,7 @@
     fastest = min(s.round_trip_time for s in server_descriptions)
     return [
         s for s in server_descriptions
-        if (s.round_trip_time - fastest) < latency_ms / 1000.]
+        if (s.round_trip_time - fastest) <= latency_ms / 1000.]
 
 
 def secondary_with_tags_server_selector(tag_sets, server_descriptions):
@@ -102,4 +123,5 @@
 
 def member_with_tags_server_selector(tag_sets, server_descriptions):
     """All near-enough members matching the tag sets."""
-    return tag_sets_server_selector(tag_sets, server_descriptions)
+    return tag_sets_server_selector(
+        tag_sets, readable_server_selector(server_descriptions))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/topology.py 
new/pymongo-3.0.2/pymongo/topology.py
--- old/pymongo-3.0.1/pymongo/topology.py       2015-04-20 22:03:23.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo/topology.py       2015-05-12 00:51:23.000000000 
+0200
@@ -40,7 +40,8 @@
         topology_description = TopologyDescription(
             topology_settings.get_topology_type(),
             topology_settings.get_server_descriptions(),
-            topology_settings.replica_set_name)
+            topology_settings.replica_set_name,
+            None)
 
         self._description = topology_description
         # Store the seed list to help diagnose errors in _error_message().
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo/topology_description.py 
new/pymongo-3.0.2/pymongo/topology_description.py
--- old/pymongo-3.0.1/pymongo/topology_description.py   2015-04-14 
20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/pymongo/topology_description.py   2015-05-12 
00:51:23.000000000 +0200
@@ -28,7 +28,12 @@
 
 
 class TopologyDescription(object):
-    def __init__(self, topology_type, server_descriptions, replica_set_name):
+    def __init__(
+            self,
+            topology_type,
+            server_descriptions,
+            replica_set_name,
+            max_election_id):
         """Represent a topology of servers.
 
         :Parameters:
@@ -36,10 +41,12 @@
           - `server_descriptions`: dict of (address, ServerDescription) for
             all seeds
           - `replica_set_name`: replica set name or None
+          - `max_election_id`: greatest electionId seen from a primary, or None
         """
         self._topology_type = topology_type
         self._replica_set_name = replica_set_name
         self._server_descriptions = server_descriptions
+        self._max_election_id = max_election_id
 
         # Is PyMongo compatible with all servers' wire protocols?
         self._incompatible_err = None
@@ -96,7 +103,11 @@
         sds = dict((address, ServerDescription(address))
                    for address in self._server_descriptions)
 
-        return TopologyDescription(topology_type, sds, self._replica_set_name)
+        return TopologyDescription(
+            topology_type,
+            sds,
+            self._replica_set_name,
+            self._max_election_id)
 
     def server_descriptions(self):
         """Dict of (address, ServerDescription)."""
@@ -112,6 +123,11 @@
         return self._replica_set_name
 
     @property
+    def max_election_id(self):
+        """Greatest electionId seen from a primary, or None."""
+        return self._max_election_id
+
+    @property
     def known_servers(self):
         """List of Servers of types besides Unknown."""
         return [s for s in self._server_descriptions.values()
@@ -146,6 +162,7 @@
     # TopologyDescription.
     topology_type = topology_description.topology_type
     set_name = topology_description.replica_set_name
+    max_election_id = topology_description.max_election_id
     server_type = server_description.server_type
 
     # Don't mutate the original dict of server descriptions; copy it.
@@ -156,7 +173,11 @@
 
     if topology_type == TOPOLOGY_TYPE.Single:
         # Single type never changes.
-        return TopologyDescription(TOPOLOGY_TYPE.Single, sds, set_name)
+        return TopologyDescription(
+            TOPOLOGY_TYPE.Single,
+            sds,
+            set_name,
+            max_election_id)
 
     if topology_type == TOPOLOGY_TYPE.Unknown:
         if server_type == SERVER_TYPE.Standalone:
@@ -174,8 +195,8 @@
             sds.pop(address)
 
         elif server_type == SERVER_TYPE.RSPrimary:
-            topology_type, set_name = _update_rs_from_primary(
-                sds, set_name, server_description)
+            topology_type, set_name, max_election_id = _update_rs_from_primary(
+                sds, set_name, server_description, max_election_id)
 
         elif server_type in (
                 SERVER_TYPE.RSSecondary,
@@ -190,8 +211,8 @@
             topology_type = _check_has_primary(sds)
 
         elif server_type == SERVER_TYPE.RSPrimary:
-            topology_type, set_name = _update_rs_from_primary(
-                sds, set_name, server_description)
+            topology_type, set_name, max_election_id = _update_rs_from_primary(
+                sds, set_name, server_description, max_election_id)
 
         elif server_type in (
                 SERVER_TYPE.RSSecondary,
@@ -205,16 +226,21 @@
             topology_type = _check_has_primary(sds)
 
     # Return updated copy.
-    return TopologyDescription(topology_type, sds, set_name)
+    return TopologyDescription(topology_type, sds, set_name, max_election_id)
 
 
-def _update_rs_from_primary(sds, replica_set_name, server_description):
+def _update_rs_from_primary(
+        sds,
+        replica_set_name,
+        server_description,
+        max_election_id):
     """Update topology description from a primary's ismaster response.
 
-    Pass in a dict of ServerDescriptions, current replica set name, and the
-    ServerDescription we are processing.
+    Pass in a dict of ServerDescriptions, current replica set name, the
+    ServerDescription we are processing, and the TopologyDescription's
+    max_election_id if any.
 
-    Returns (new topology type, new replica_set_name).
+    Returns (new topology type, new replica_set_name, new max_election_id).
     """
     if replica_set_name is None:
         replica_set_name = server_description.replica_set_name
@@ -223,7 +249,16 @@
         # We found a primary but it doesn't have the replica_set_name
         # provided by the user.
         sds.pop(server_description.address)
-        return _check_has_primary(sds), replica_set_name
+        return _check_has_primary(sds), replica_set_name, max_election_id
+
+    if server_description.election_id is not None:
+        if max_election_id and max_election_id > 
server_description.election_id:
+            # Stale primary, set to type Unknown.
+            address = server_description.address
+            sds[address] = ServerDescription(address)
+            return _check_has_primary(sds), replica_set_name, max_election_id
+
+        max_election_id = server_description.election_id
 
     # We've heard from the primary. Is it the same primary as before?
     for server in sds.values():
@@ -247,7 +282,7 @@
 
     # If the host list differs from the seed list, we may not have a primary
     # after all.
-    return _check_has_primary(sds), replica_set_name
+    return _check_has_primary(sds), replica_set_name, max_election_id
 
 
 def _update_rs_with_primary_from_member(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/pymongo.egg-info/PKG-INFO 
new/pymongo-3.0.2/pymongo.egg-info/PKG-INFO
--- old/pymongo-3.0.1/pymongo.egg-info/PKG-INFO 2015-04-21 22:37:57.000000000 
+0200
+++ new/pymongo-3.0.2/pymongo.egg-info/PKG-INFO 2015-05-12 21:28:05.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pymongo
-Version: 3.0.1
+Version: 3.0.2
 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-3.0.1/pymongo.egg-info/SOURCES.txt 
new/pymongo-3.0.2/pymongo.egg-info/SOURCES.txt
--- old/pymongo-3.0.1/pymongo.egg-info/SOURCES.txt      2015-04-21 
22:37:57.000000000 +0200
+++ new/pymongo-3.0.2/pymongo.egg-info/SOURCES.txt      2015-05-12 
21:28:05.000000000 +0200
@@ -47,6 +47,7 @@
 doc/api/bson/dbref.rst
 doc/api/bson/errors.rst
 doc/api/bson/index.rst
+doc/api/bson/int64.rst
 doc/api/bson/json_util.rst
 doc/api/bson/max_key.rst
 doc/api/bson/min_key.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/setup.py new/pymongo-3.0.2/setup.py
--- old/pymongo-3.0.1/setup.py  2015-04-21 22:28:25.000000000 +0200
+++ new/pymongo-3.0.2/setup.py  2015-05-12 21:21:41.000000000 +0200
@@ -26,7 +26,7 @@
 from distutils.errors import DistutilsPlatformError, DistutilsExecError
 from distutils.core import Extension
 
-version = "3.0.1"
+version = "3.0.2"
 
 f = open("README.rst")
 try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/test/__init__.py 
new/pymongo-3.0.2/test/__init__.py
--- old/pymongo-3.0.1/test/__init__.py  2015-04-21 00:04:45.000000000 +0200
+++ new/pymongo-3.0.2/test/__init__.py  2015-05-12 00:51:23.000000000 +0200
@@ -127,8 +127,13 @@
                 self.rs_client = pymongo.MongoClient(
                     pair, replicaSet=self.replica_set_name)
 
-                self.nodes = set([partition_node(node)
-                                  for node in self.ismaster.get('hosts', [])])
+                nodes = [partition_node(node)
+                         for node in self.ismaster.get('hosts', [])]
+                nodes.extend([partition_node(node)
+                              for node in self.ismaster.get('passives', [])])
+                nodes.extend([partition_node(node)
+                              for node in self.ismaster.get('arbiters', [])])
+                self.nodes = set(nodes)
 
             self.rs_or_standalone_client = self.rs_client or self.client
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/test/test_discovery_and_monitoring.py 
new/pymongo-3.0.2/test/test_discovery_and_monitoring.py
--- old/pymongo-3.0.1/test/test_discovery_and_monitoring.py     2015-04-14 
20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/test/test_discovery_and_monitoring.py     2015-05-12 
00:51:23.000000000 +0200
@@ -14,13 +14,13 @@
 
 """Test the topology module."""
 
-import json
 import os
 import sys
 import threading
 
 sys.path[0:0] = [""]
 
+from bson import json_util
 from pymongo import common
 from pymongo.topology import Topology
 from pymongo.topology_description import TOPOLOGY_TYPE
@@ -175,7 +175,7 @@
 
         for filename in filenames:
             with open(os.path.join(dirpath, filename)) as scenario_stream:
-                scenario_def = json.load(scenario_stream)
+                scenario_def = json_util.loads(scenario_stream.read())
 
             # Construct test from scenario.
             new_test = create_test(scenario_def)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/test/test_mongos_load_balancing.py 
new/pymongo-3.0.2/test/test_mongos_load_balancing.py
--- old/pymongo-3.0.1/test/test_mongos_load_balancing.py        2015-04-14 
20:25:10.000000000 +0200
+++ new/pymongo-3.0.2/test/test_mongos_load_balancing.py        2015-05-12 
00:51:23.000000000 +0200
@@ -75,7 +75,7 @@
         # Latencies in seconds.
         mock_client.mock_rtts['a:1'] = 0.020
         mock_client.mock_rtts['b:2'] = 0.025
-        mock_client.mock_rtts['c:3'] = 0.040
+        mock_client.mock_rtts['c:3'] = 0.045
         return mock_client
 
     def test_lazy_connect(self):
@@ -140,6 +140,7 @@
 
     def test_local_threshold(self):
         client = connected(self.mock_client(localThresholdMS=30))
+        self.assertEqual(30, client.local_threshold_ms)
         wait_until(lambda: len(client.nodes) == 3, 'connect to all mongoses')
         topology = client._topology
 
@@ -147,6 +148,22 @@
         self.assertEqual(set([('a', 1), ('b', 2), ('c', 3)]),
                          writable_addresses(topology))
 
+        # No error
+        client.admin.command('ismaster')
+
+        client = connected(self.mock_client(localThresholdMS=0))
+        self.assertEqual(0, client.local_threshold_ms)
+        # No error
+        client.db.command('ismaster')
+        # Our chosen mongos goes down.
+        client.kill_host('%s:%s' % next(iter(client.nodes)))
+        try:
+            client.db.command('ismaster')
+        except:
+            pass
+        # No error
+        client.db.command('ismaster')
+
     def test_load_balancing(self):
         # Although the server selection JSON tests already prove that
         # select_servers works for sharded topologies, here we do an end-to-end
@@ -167,7 +184,7 @@
         self.assertEqual(set([('a', 1), ('b', 2)]),
                          writable_addresses(topology))
 
-        client.mock_rtts['a:1'] = 0.040
+        client.mock_rtts['a:1'] = 0.045
 
         # Discover only b is within latency window.
         wait_until(lambda: set([('b', 2)]) == writable_addresses(topology),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pymongo-3.0.1/test/test_read_preferences.py 
new/pymongo-3.0.2/test/test_read_preferences.py
--- old/pymongo-3.0.1/test/test_read_preferences.py     2015-04-21 
01:17:13.000000000 +0200
+++ new/pymongo-3.0.2/test/test_read_preferences.py     2015-05-12 
00:51:23.000000000 +0200
@@ -15,8 +15,10 @@
 """Test the replica_set_connection module."""
 
 import contextlib
+import copy
 import random
 import sys
+import pickle
 
 sys.path[0:0] = [""]
 
@@ -29,7 +31,8 @@
                                       Primary, PrimaryPreferred,
                                       Secondary, SecondaryPreferred,
                                       Nearest, _ServerMode)
-from pymongo.server_selectors import any_server_selector
+from pymongo.server_description import ServerDescription
+from pymongo.server_selectors import readable_server_selector
 from pymongo.server_type import SERVER_TYPE
 from pymongo.write_concern import WriteConcern
 
@@ -41,10 +44,22 @@
                   unittest,
                   db_user,
                   db_pwd)
-from test.utils import single_client, one, wait_until, rs_client
+from test.utils import connected, single_client, one, wait_until, rs_client
 from test.version import Version
 
 
+class TestReadPreferenceObjects(unittest.TestCase):
+    prefs = [Primary(), Secondary(), Nearest(tag_sets=[{'a': 1}, {'b': 2}])]
+
+    def test_pickle(self):
+        for pref in self.prefs:
+            self.assertEqual(pref, pickle.loads(pickle.dumps(pref)))
+
+    def test_copy(self):
+        for pref in self.prefs:
+            self.assertEqual(pref, copy.copy(pref))
+
+
 class TestReadPreferencesBase(TestReplicaSetClientBase):
 
     def setUp(self):
@@ -83,7 +98,7 @@
     def assertReadsFrom(self, expected, **kwargs):
         c = rs_client(**kwargs)
         wait_until(
-            lambda: len(c.nodes) == self.w,
+            lambda: len(c.nodes - c.arbiters) == self.w,
             "discovered all nodes")
 
         used = self.read_from_which_kind(c)
@@ -189,6 +204,33 @@
             localthresholdms=666
         ).local_threshold_ms)
 
+        self.assertEqual(0, rs_client(
+            localthresholdms=0
+        ).local_threshold_ms)
+
+        self.assertRaises(ValueError,
+                          rs_client,
+                          localthresholdms=-1)
+
+    def test_zero_latency(self):
+        ping_times = set()
+        # Generate unique ping times.
+        while len(ping_times) < len(self.client.nodes):
+            ping_times.add(random.random())
+        for ping_time, host in zip(ping_times, self.client.nodes):
+            ServerDescription._host_to_round_trip_time[host] = ping_time
+        try:
+            client = connected(
+                rs_client(readPreference='nearest', localThresholdMS=0))
+            wait_until(
+                lambda: client.nodes == self.client.nodes,
+                "discovered all nodes")
+            host = self.read_from_which_host(client)
+            for _ in range(5):
+                self.assertEqual(host, self.read_from_which_host(client))
+        finally:
+            ServerDescription._host_to_round_trip_time.clear()
+
     def test_primary(self):
         self.assertReadsFrom(
             'primary', read_preference=ReadPreference.PRIMARY)
@@ -235,7 +277,8 @@
         latencies = ', '.join(
             '%s: %dms' % (server.description.address,
                           server.description.round_trip_time)
-            for server in 
c._get_topology().select_servers(any_server_selector))
+            for server in c._get_topology().select_servers(
+                readable_server_selector))
 
         self.assertFalse(
             not_used,


Reply via email to