From 56925fd97f0d1e5514d3ca738289c5179f522dde Mon Sep 17 00:00:00 2001 From: Shane Harvey Date: Fri, 15 Jan 2021 17:11:15 -0800 Subject: [PATCH] PYTHON-1321 Remove MongoReplicaSetClient (#552) --- doc/api/pymongo/index.rst | 5 - doc/api/pymongo/mongo_replica_set_client.rst | 32 -- doc/changelog.rst | 1 + doc/migrate-to-pymongo4.rst | 9 + pymongo/__init__.py | 1 - pymongo/common.py | 2 +- pymongo/mongo_client.py | 6 +- pymongo/mongo_replica_set_client.py | 48 --- pymongo/read_preferences.py | 4 +- test/test_gridfs.py | 18 +- test/test_gridfs_bucket.py | 18 +- test/test_read_preferences.py | 16 +- test/test_replica_set_client.py | 376 ------------------- 13 files changed, 37 insertions(+), 499 deletions(-) delete mode 100644 doc/api/pymongo/mongo_replica_set_client.rst delete mode 100644 pymongo/mongo_replica_set_client.py delete mode 100644 test/test_replica_set_client.py diff --git a/doc/api/pymongo/index.rst b/doc/api/pymongo/index.rst index fb48c94ea..51bcc708a 100644 --- a/doc/api/pymongo/index.rst +++ b/doc/api/pymongo/index.rst @@ -9,10 +9,6 @@ Alias for :class:`pymongo.mongo_client.MongoClient`. - .. data:: MongoReplicaSetClient - - Alias for :class:`pymongo.mongo_replica_set_client.MongoReplicaSetClient`. - .. data:: ReadPreference Alias for :class:`pymongo.read_preferences.ReadPreference`. @@ -46,7 +42,6 @@ Sub-modules: errors message mongo_client - mongo_replica_set_client monitoring operations pool diff --git a/doc/api/pymongo/mongo_replica_set_client.rst b/doc/api/pymongo/mongo_replica_set_client.rst deleted file mode 100644 index a614f2555..000000000 --- a/doc/api/pymongo/mongo_replica_set_client.rst +++ /dev/null @@ -1,32 +0,0 @@ -:mod:`mongo_replica_set_client` -- Tools for connecting to a MongoDB replica set -================================================================================ - -.. automodule:: pymongo.mongo_replica_set_client - :synopsis: Tools for connecting to a MongoDB replica set - - .. autoclass:: pymongo.mongo_replica_set_client.MongoReplicaSetClient(hosts_or_uri, document_class=dict, tz_aware=False, connect=True, **kwargs) - - .. automethod:: close - - .. describe:: c[db_name] || c.db_name - - Get the `db_name` :class:`~pymongo.database.Database` on :class:`MongoReplicaSetClient` `c`. - - Raises :class:`~pymongo.errors.InvalidName` if an invalid database name is used. - - .. autoattribute:: primary - .. autoattribute:: secondaries - .. autoattribute:: arbiters - .. autoattribute:: max_pool_size - .. autoattribute:: max_bson_size - .. autoattribute:: max_message_size - .. autoattribute:: local_threshold_ms - .. autoattribute:: codec_options - .. autoattribute:: read_preference - .. autoattribute:: write_concern - .. automethod:: drop_database - .. automethod:: get_database - .. automethod:: close_cursor - .. automethod:: kill_cursors - .. automethod:: set_cursor_manager - .. automethod:: get_default_database diff --git a/doc/changelog.rst b/doc/changelog.rst index 060ee4376..98fcd4bce 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -25,6 +25,7 @@ Breaking Changes in 4.0 - Removed :meth:`pymongo.database.Database.collection_names`. - Removed :meth:`pymongo.collection.Collection.parallel_scan`. - Removed :mod:`pymongo.thread_util`. +- Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`. Notable improvements .................... diff --git a/doc/migrate-to-pymongo4.rst b/doc/migrate-to-pymongo4.rst index 2c2927c41..8fcd52df0 100644 --- a/doc/migrate-to-pymongo4.rst +++ b/doc/migrate-to-pymongo4.rst @@ -50,6 +50,15 @@ Warnings can also be changed to errors:: .. note:: Not all deprecated features raise :exc:`DeprecationWarning` when used. See `Removed features with no migration path`_. +MongoReplicaSetClient +--------------------- + +Removed :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`. +Since PyMongo 3.0, ``MongoReplicaSetClient`` has been identical to +:class:`pymongo.mongo_client.MongoClient`. Applications can simply replace +``MongoReplicaSetClient`` with :class:`pymongo.mongo_client.MongoClient` and +get the same behavior. + MongoClient ----------- diff --git a/pymongo/__init__.py b/pymongo/__init__.py index 796ef15db..1ce7f82c1 100644 --- a/pymongo/__init__.py +++ b/pymongo/__init__.py @@ -89,7 +89,6 @@ from pymongo.common import (MIN_SUPPORTED_WIRE_VERSION, MAX_SUPPORTED_WIRE_VERSION) from pymongo.cursor import CursorType from pymongo.mongo_client import MongoClient -from pymongo.mongo_replica_set_client import MongoReplicaSetClient from pymongo.operations import (IndexModel, InsertOne, DeleteOne, diff --git a/pymongo/common.py b/pymongo/common.py index a4d91b7b1..f45bc7128 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -364,7 +364,7 @@ def validate_read_preference(dummy, value): def validate_read_preference_mode(dummy, value): - """Validate read preference mode for a MongoReplicaSetClient. + """Validate read preference mode for a MongoClient. .. versionchanged:: 3.5 Returns the original ``value`` instead of the validated read preference diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 7a45c0f85..de78188e1 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -1001,8 +1001,7 @@ class MongoClient(common.BaseObject): `replicaSet` option. .. versionadded:: 3.0 - MongoClient gained this property in version 3.0 when - MongoReplicaSetClient's functionality was merged in. + MongoClient gained this property in version 3.0. """ return self._topology.get_primary() @@ -1015,8 +1014,7 @@ class MongoClient(common.BaseObject): client was created without the `replicaSet` option. .. versionadded:: 3.0 - MongoClient gained this property in version 3.0 when - MongoReplicaSetClient's functionality was merged in. + MongoClient gained this property in version 3.0. """ return self._topology.get_secondaries() diff --git a/pymongo/mongo_replica_set_client.py b/pymongo/mongo_replica_set_client.py deleted file mode 100644 index c9436c24e..000000000 --- a/pymongo/mongo_replica_set_client.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright 2011-2015 MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you -# may not use this file except in compliance with the License. You -# may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing -# permissions and limitations under the License. - -"""Deprecated. See :doc:`/examples/high_availability`.""" - -import warnings - -from pymongo import mongo_client - - -class MongoReplicaSetClient(mongo_client.MongoClient): - """Deprecated alias for :class:`~pymongo.mongo_client.MongoClient`. - - :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient` - will be removed in a future version of PyMongo. - - .. versionchanged:: 3.0 - :class:`~pymongo.mongo_client.MongoClient` is now the one and only - client class for a standalone server, mongos, or replica set. - It includes the functionality that had been split into - :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`: it - can connect to a replica set, discover all its members, and monitor - the set for stepdowns, elections, and reconfigs. - - The ``refresh`` method is removed from - :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`, - as are the ``seeds`` and ``hosts`` properties. - """ - def __init__(self, *args, **kwargs): - warnings.warn('MongoReplicaSetClient is deprecated, use MongoClient' - ' to connect to a replica set', - DeprecationWarning, stacklevel=2) - - super(MongoReplicaSetClient, self).__init__(*args, **kwargs) - - def __repr__(self): - return "MongoReplicaSetClient(%s)" % (self._repr_helper(),) diff --git a/pymongo/read_preferences.py b/pymongo/read_preferences.py index 53e898017..5ba833e53 100644 --- a/pymongo/read_preferences.py +++ b/pymongo/read_preferences.py @@ -38,7 +38,7 @@ _MONGOS_MODES = ( def _validate_tag_sets(tag_sets): - """Validate tag sets for a MongoReplicaSetClient. + """Validate tag sets for a MongoClient. """ if tag_sets is None: return tag_sets @@ -144,7 +144,7 @@ class _ServerMode(object): To specify a priority-order for tag sets, provide a list of tag sets: ``[{'dc': 'ny'}, {'dc': 'la'}, {}]``. A final, empty tag set, ``{}``, means "read from any member that matches the mode, - ignoring tags." MongoReplicaSetClient tries each set of tags in turn + ignoring tags." MongoClient tries each set of tags in turn until it finds a set of tags with at least one matching member. .. seealso:: `Data-Center Awareness diff --git a/test/test_gridfs.py b/test/test_gridfs.py index 82422f968..0d05e0b78 100644 --- a/test/test_gridfs.py +++ b/test/test_gridfs.py @@ -28,11 +28,10 @@ from bson.binary import Binary from bson.py3compat import StringIO, string_type from pymongo.mongo_client import MongoClient from pymongo.errors import (ConfigurationError, - ConnectionFailure, + NotMasterError, ServerSelectionTimeoutError) from pymongo.read_preferences import ReadPreference from gridfs.errors import CorruptGridFile, FileExists, NoFile -from test.test_replica_set_client import TestReplicaSetClientBase from test import (client_context, unittest, IntegrationTest) @@ -487,7 +486,7 @@ class TestGridfs(IntegrationTest): self.assertIsNone(gout.md5) -class TestGridfsReplicaSet(TestReplicaSetClientBase): +class TestGridfsReplicaSet(IntegrationTest): @classmethod @client_context.require_secondaries_count(1) @@ -500,7 +499,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase): def test_gridfs_replica_set(self): rsc = rs_client( - w=self.w, + w=client_context.w, read_preference=ReadPreference.SECONDARY) fs = gridfs.GridFS(rsc.gfsreplica, 'gfsreplicatest') @@ -513,10 +512,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase): self.assertEqual(b'foo', content) def test_gridfs_secondary(self): - primary_host, primary_port = self.primary - primary_connection = single_client(primary_host, primary_port) - - secondary_host, secondary_port = one(self.secondaries) + secondary_host, secondary_port = one(self.client.secondaries) secondary_connection = single_client( secondary_host, secondary_port, read_preference=ReadPreference.SECONDARY) @@ -526,12 +522,12 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase): fs = gridfs.GridFS(secondary_connection.gfsreplica, 'gfssecondarytest') # This won't detect secondary, raises error - self.assertRaises(ConnectionFailure, fs.put, b'foo') + self.assertRaises(NotMasterError, fs.put, b'foo') def test_gridfs_secondary_lazy(self): # Should detect it's connected to secondary and not attempt to # create index. - secondary_host, secondary_port = one(self.secondaries) + secondary_host, secondary_port = one(self.client.secondaries) client = single_client( secondary_host, secondary_port, @@ -543,7 +539,7 @@ class TestGridfsReplicaSet(TestReplicaSetClientBase): # Connects, doesn't create index. self.assertRaises(NoFile, fs.get_last_version) - self.assertRaises(ConnectionFailure, fs.put, 'data') + self.assertRaises(NotMasterError, fs.put, 'data') if __name__ == "__main__": diff --git a/test/test_gridfs_bucket.py b/test/test_gridfs_bucket.py index 21feda719..17b5364bb 100644 --- a/test/test_gridfs_bucket.py +++ b/test/test_gridfs_bucket.py @@ -30,14 +30,13 @@ from bson.py3compat import StringIO, string_type from bson.son import SON from gridfs.errors import NoFile, CorruptGridFile from pymongo.errors import (ConfigurationError, - ConnectionFailure, + NotMasterError, ServerSelectionTimeoutError) from pymongo.mongo_client import MongoClient from pymongo.read_preferences import ReadPreference from test import (client_context, unittest, IntegrationTest) -from test.test_replica_set_client import TestReplicaSetClientBase from test.utils import (ignore_deprecations, joinall, one, @@ -504,7 +503,7 @@ class TestGridfs(IntegrationTest): self.assertIsNone(gout.md5) -class TestGridfsBucketReplicaSet(TestReplicaSetClientBase): +class TestGridfsBucketReplicaSet(IntegrationTest): @classmethod @client_context.require_secondaries_count(1) @@ -517,7 +516,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase): def test_gridfs_replica_set(self): rsc = rs_client( - w=self.w, + w=client_context.w, read_preference=ReadPreference.SECONDARY) gfs = gridfs.GridFSBucket(rsc.gfsbucketreplica, 'gfsbucketreplicatest') @@ -526,10 +525,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase): self.assertEqual(b'foo', content) def test_gridfs_secondary(self): - primary_host, primary_port = self.primary - primary_connection = single_client(primary_host, primary_port) - - secondary_host, secondary_port = one(self.secondaries) + secondary_host, secondary_port = one(self.client.secondaries) secondary_connection = single_client( secondary_host, secondary_port, read_preference=ReadPreference.SECONDARY) @@ -540,13 +536,13 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase): secondary_connection.gfsbucketreplica, 'gfsbucketsecondarytest') # This won't detect secondary, raises error - self.assertRaises(ConnectionFailure, gfs.upload_from_stream, + self.assertRaises(NotMasterError, gfs.upload_from_stream, "test_filename", b'foo') def test_gridfs_secondary_lazy(self): # Should detect it's connected to secondary and not attempt to # create index. - secondary_host, secondary_port = one(self.secondaries) + secondary_host, secondary_port = one(self.client.secondaries) client = single_client( secondary_host, secondary_port, @@ -560,7 +556,7 @@ class TestGridfsBucketReplicaSet(TestReplicaSetClientBase): # Connects, doesn't create index. self.assertRaises(NoFile, gfs.open_download_stream_by_name, "test_filename") - self.assertRaises(ConnectionFailure, gfs.upload_from_stream, + self.assertRaises(NotMasterError, gfs.upload_from_stream, "test_filename", b'data') diff --git a/test/test_read_preferences.py b/test/test_read_preferences.py index a85cb03d2..2a8eb96e9 100644 --- a/test/test_read_preferences.py +++ b/test/test_read_preferences.py @@ -39,10 +39,10 @@ from pymongo.write_concern import WriteConcern from test import (SkipTest, client_context, + IntegrationTest, unittest, db_user, db_pwd) -from test.test_replica_set_client import TestReplicaSetClientBase from test.utils import (connected, ignore_deprecations, one, @@ -87,7 +87,7 @@ class TestReadPreferenceObjects(unittest.TestCase): self.assertEqual(pref, copy.deepcopy(pref)) -class TestReadPreferencesBase(TestReplicaSetClientBase): +class TestReadPreferencesBase(IntegrationTest): @classmethod @client_context.require_secondaries_count(1) @@ -100,7 +100,7 @@ class TestReadPreferencesBase(TestReplicaSetClientBase): self.client.pymongo_test.test.drop() self.client.get_database( "pymongo_test", - write_concern=WriteConcern(w=self.w)).test.insert_many( + write_concern=WriteConcern(w=client_context.w)).test.insert_many( [{'_id': i} for i in range(10)]) self.addCleanup(self.client.pymongo_test.test.drop) @@ -130,7 +130,7 @@ class TestReadPreferencesBase(TestReplicaSetClientBase): def assertReadsFrom(self, expected, **kwargs): c = rs_client(**kwargs) wait_until( - lambda: len(c.nodes - c.arbiters) == self.w, + lambda: len(c.nodes - c.arbiters) == client_context.w, "discovered all nodes") used = self.read_from_which_kind(c) @@ -284,7 +284,7 @@ class TestReadPreferences(TestReadPreferencesBase): read_preference=ReadPreference.NEAREST, localThresholdMS=10000) # 10 seconds - data_members = set(self.hosts).difference(set(self.arbiters)) + data_members = {self.client.primary} | self.client.secondaries # This is a probabilistic test; track which members we've read from so # far, and keep reading until we've used all the members or give up. @@ -347,7 +347,7 @@ _PREF_MAP = [ ] -class TestCommandAndReadPreference(TestReplicaSetClientBase): +class TestCommandAndReadPreference(IntegrationTest): @classmethod @client_context.require_secondaries_count(1) @@ -355,7 +355,7 @@ class TestCommandAndReadPreference(TestReplicaSetClientBase): super(TestCommandAndReadPreference, cls).setUpClass() cls.c = ReadPrefTester( client_context.pair, - replicaSet=cls.name, + replicaSet=client_context.replica_set_name, # Ignore round trip times, to test ReadPreference modes only. localThresholdMS=1000*1000) if client_context.auth_enabled: @@ -363,7 +363,7 @@ class TestCommandAndReadPreference(TestReplicaSetClientBase): cls.client_version = Version.from_client(cls.c) # mapReduce and group fail with no collection coll = cls.c.pymongo_test.get_collection( - 'test', write_concern=WriteConcern(w=cls.w)) + 'test', write_concern=WriteConcern(w=client_context.w)) coll.insert_one({}) @classmethod diff --git a/test/test_replica_set_client.py b/test/test_replica_set_client.py deleted file mode 100644 index 619499c39..000000000 --- a/test/test_replica_set_client.py +++ /dev/null @@ -1,376 +0,0 @@ -# Copyright 2011-present MongoDB, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Test the mongo_replica_set_client module.""" - -import sys -import warnings -import time - -sys.path[0:0] = [""] - -from bson.codec_options import CodecOptions -from bson.son import SON -from pymongo.common import MAX_SUPPORTED_WIRE_VERSION, partition_node -from pymongo.errors import (AutoReconnect, - ConfigurationError, - ConnectionFailure, - NetworkTimeout, - NotMasterError, - OperationFailure) -from pymongo.mongo_client import MongoClient -from pymongo.mongo_replica_set_client import MongoReplicaSetClient -from pymongo.read_preferences import ReadPreference, Secondary, Nearest -from pymongo.write_concern import WriteConcern -from test import (client_context, - client_knobs, - IntegrationTest, - unittest, - SkipTest, - db_pwd, - db_user, - MockClientTest, - HAVE_IPADDRESS) -from test.pymongo_mocks import MockClient -from test.utils import (connected, - delay, - ignore_deprecations, - one, - rs_client, - single_client, - wait_until) - - -class TestReplicaSetClientBase(IntegrationTest): - - @classmethod - @client_context.require_replica_set - def setUpClass(cls): - super(TestReplicaSetClientBase, cls).setUpClass() - cls.name = client_context.replica_set_name - cls.w = client_context.w - - ismaster = client_context.ismaster - cls.hosts = set(partition_node(h.lower()) for h in ismaster['hosts']) - cls.arbiters = set(partition_node(h) - for h in ismaster.get("arbiters", [])) - - repl_set_status = client_context.client.admin.command( - 'replSetGetStatus') - primary_info = [ - m for m in repl_set_status['members'] - if m['stateStr'] == 'PRIMARY' - ][0] - - cls.primary = partition_node(primary_info['name'].lower()) - cls.secondaries = set( - partition_node(m['name'].lower()) - for m in repl_set_status['members'] - if m['stateStr'] == 'SECONDARY') - - -class TestReplicaSetClient(TestReplicaSetClientBase): - def test_deprecated(self): - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - with self.assertRaises(DeprecationWarning): - MongoReplicaSetClient() - - def test_connect(self): - client = MongoClient( - client_context.pair, - replicaSet='fdlksjfdslkjfd', - serverSelectionTimeoutMS=100) - - with self.assertRaises(ConnectionFailure): - client.test.test.find_one() - - def test_repr(self): - with ignore_deprecations(): - client = MongoReplicaSetClient( - client_context.host, - client_context.port, - replicaSet=self.name) - - self.assertIn("MongoReplicaSetClient(host=[", repr(client)) - self.assertIn(client_context.pair, repr(client)) - - def test_properties(self): - c = client_context.client - c.admin.command('ping') - - wait_until(lambda: c.primary == self.primary, "discover primary") - wait_until(lambda: c.arbiters == self.arbiters, "discover arbiters") - wait_until(lambda: c.secondaries == self.secondaries, - "discover secondaries") - - self.assertEqual(c.primary, self.primary) - self.assertEqual(c.secondaries, self.secondaries) - self.assertEqual(c.arbiters, self.arbiters) - self.assertEqual(c.max_pool_size, 100) - - # Make sure MongoClient's properties are copied to Database and - # Collection. - for obj in c, c.pymongo_test, c.pymongo_test.test: - self.assertEqual(obj.codec_options, CodecOptions()) - self.assertEqual(obj.read_preference, ReadPreference.PRIMARY) - self.assertEqual(obj.write_concern, WriteConcern()) - - cursor = c.pymongo_test.test.find() - self.assertEqual( - ReadPreference.PRIMARY, cursor._read_preference()) - - tag_sets = [{'dc': 'la', 'rack': '2'}, {'foo': 'bar'}] - secondary = Secondary(tag_sets=tag_sets) - c = rs_client( - maxPoolSize=25, - document_class=SON, - tz_aware=True, - read_preference=secondary, - localThresholdMS=77, - j=True) - - self.assertEqual(c.max_pool_size, 25) - - for obj in c, c.pymongo_test, c.pymongo_test.test: - self.assertEqual(obj.codec_options, CodecOptions(SON, True)) - self.assertEqual(obj.read_preference, secondary) - self.assertEqual(obj.write_concern, WriteConcern(j=True)) - - cursor = c.pymongo_test.test.find() - self.assertEqual( - secondary, cursor._read_preference()) - - nearest = Nearest(tag_sets=[{'dc': 'ny'}, {}]) - cursor = c.pymongo_test.get_collection( - "test", read_preference=nearest).find() - - self.assertEqual(nearest, cursor._read_preference()) - self.assertEqual(c.max_bson_size, 16777216) - c.close() - - @client_context.require_secondaries_count(1) - def test_timeout_does_not_mark_member_down(self): - # If a query times out, the client shouldn't mark the member "down". - - # Disable background refresh. - with client_knobs(heartbeat_frequency=999999): - c = rs_client(socketTimeoutMS=1000, w=self.w) - collection = c.pymongo_test.test - collection.insert_one({}) - - # Query the primary. - self.assertRaises( - NetworkTimeout, - collection.find_one, - {'$where': delay(1.5)}) - - self.assertTrue(c.primary) - collection.find_one() # No error. - - coll = collection.with_options( - read_preference=ReadPreference.SECONDARY) - - # Query the secondary. - self.assertRaises( - NetworkTimeout, - coll.find_one, - {'$where': delay(1.5)}) - - self.assertTrue(c.secondaries) - - # No error. - coll.find_one() - - @client_context.require_ipv6 - def test_ipv6(self): - if client_context.tls: - if not HAVE_IPADDRESS: - raise SkipTest("Need the ipaddress module to test with SSL") - - port = client_context.port - c = rs_client("mongodb://[::1]:%d" % (port,)) - - # Client switches to IPv4 once it has first ismaster response. - msg = 'discovered primary with IPv4 address "%r"' % (self.primary,) - wait_until(lambda: c.primary == self.primary, msg) - - # Same outcome with both IPv4 and IPv6 seeds. - c = rs_client("mongodb://[::1]:%d,localhost:%d" % (port, port)) - - wait_until(lambda: c.primary == self.primary, msg) - - if client_context.auth_enabled: - auth_str = "%s:%s@" % (db_user, db_pwd) - else: - auth_str = "" - - uri = "mongodb://%slocalhost:%d,[::1]:%d" % (auth_str, port, port) - client = rs_client(uri) - client.pymongo_test.test.insert_one({"dummy": u"object"}) - client.pymongo_test_bernie.test.insert_one({"dummy": u"object"}) - - dbs = client.list_database_names() - self.assertTrue("pymongo_test" in dbs) - self.assertTrue("pymongo_test_bernie" in dbs) - client.close() - - def _test_kill_cursor_explicit(self, read_pref): - with client_knobs(kill_cursor_frequency=0.01): - c = rs_client(read_preference=read_pref, w=self.w) - db = c.pymongo_test - db.drop_collection("test") - - test = db.test - test.insert_many([{"i": i} for i in range(20)]) - - # Partially evaluate cursor so it's left alive, then kill it - cursor = test.find().batch_size(10) - next(cursor) - self.assertNotEqual(0, cursor.cursor_id) - - if read_pref == ReadPreference.PRIMARY: - msg = "Expected cursor's address to be %s, got %s" % ( - c.primary, cursor.address) - - self.assertEqual(cursor.address, c.primary, msg) - else: - self.assertNotEqual( - cursor.address, c.primary, - "Expected cursor's address not to be primary") - - cursor_id = cursor.cursor_id - - # Cursor dead on server - trigger a getMore on the same cursor_id - # and check that the server returns an error. - cursor2 = cursor.clone() - cursor2._Cursor__id = cursor_id - - if sys.platform.startswith('java') or 'PyPy' in sys.version: - # Explicitly kill cursor. - cursor.close() - else: - # Implicitly kill it in CPython. - del cursor - - time.sleep(5) - self.assertRaises(OperationFailure, lambda: list(cursor2)) - - def test_kill_cursor_explicit_primary(self): - self._test_kill_cursor_explicit(ReadPreference.PRIMARY) - - @client_context.require_secondaries_count(1) - def test_kill_cursor_explicit_secondary(self): - self._test_kill_cursor_explicit(ReadPreference.SECONDARY) - - @client_context.require_secondaries_count(1) - def test_not_master_error(self): - secondary_address = one(self.secondaries) - direct_client = single_client(*secondary_address) - - with self.assertRaises(NotMasterError): - direct_client.pymongo_test.collection.insert_one({}) - - db = direct_client.get_database( - "pymongo_test", write_concern=WriteConcern(w=0)) - with self.assertRaises(NotMasterError): - db.collection.insert_one({}) - - -class TestReplicaSetWireVersion(MockClientTest): - - @client_context.require_connection - @client_context.require_no_auth - def test_wire_version(self): - c = MockClient( - standalones=[], - members=['a:1', 'b:2', 'c:3'], - mongoses=[], - host='a:1', - replicaSet='rs', - connect=False) - self.addCleanup(c.close) - - c.set_wire_version_range('a:1', 3, 7) - c.set_wire_version_range('b:2', 2, 3) - c.set_wire_version_range('c:3', 3, 4) - c.db.command('ismaster') # Connect. - - # A secondary doesn't overlap with us. - c.set_wire_version_range('b:2', - MAX_SUPPORTED_WIRE_VERSION + 1, - MAX_SUPPORTED_WIRE_VERSION + 2) - - def raises_configuration_error(): - try: - c.db.collection.find_one() - return False - except ConfigurationError: - return True - - wait_until(raises_configuration_error, - 'notice we are incompatible with server') - - self.assertRaises(ConfigurationError, c.db.collection.insert_one, {}) - - -class TestReplicaSetClientInternalIPs(MockClientTest): - - @client_context.require_connection - def test_connect_with_internal_ips(self): - # Client is passed an IP it can reach, 'a:1', but the RS config - # only contains unreachable IPs like 'internal-ip'. PYTHON-608. - client = MockClient( - standalones=[], - members=['a:1'], - mongoses=[], - ismaster_hosts=['internal-ip:27017'], - host='a:1', - replicaSet='rs', - serverSelectionTimeoutMS=100) - self.addCleanup(client.close) - with self.assertRaises(AutoReconnect) as context: - connected(client) - - self.assertIn("Could not reach any servers in [('internal-ip', 27017)]." - " Replica set is configured with internal hostnames or IPs?", - str(context.exception)) - -class TestReplicaSetClientMaxWriteBatchSize(MockClientTest): - - @client_context.require_connection - def test_max_write_batch_size(self): - c = MockClient( - standalones=[], - members=['a:1', 'b:2'], - mongoses=[], - host='a:1', - replicaSet='rs', - connect=False) - self.addCleanup(c.close) - - c.set_max_write_batch_size('a:1', 1) - c.set_max_write_batch_size('b:2', 2) - - # Uses primary's max batch size. - self.assertEqual(c.max_write_batch_size, 1) - - # b becomes primary. - c.mock_primary = 'b:2' - wait_until(lambda: c.max_write_batch_size == 2, - 'update max_write_batch_size') - - -if __name__ == "__main__": - unittest.main()