From e4c8d17a8f014770a72ecc535e43b9f79f71d8a5 Mon Sep 17 00:00:00 2001 From: Bernie Hackett Date: Thu, 25 Jun 2015 11:24:44 -0700 Subject: [PATCH] PYTHON-955 - Backport connect option. --- doc/migrate-to-pymongo3.rst | 36 +++++++++++++++++++++++++++++ pymongo/common.py | 4 +++- pymongo/connection.py | 6 ++--- pymongo/mongo_client.py | 6 +++-- pymongo/mongo_replica_set_client.py | 5 +++- test/test_client.py | 9 ++++++++ test/test_replica_set_client.py | 10 ++++++++ 7 files changed, 69 insertions(+), 7 deletions(-) diff --git a/doc/migrate-to-pymongo3.rst b/doc/migrate-to-pymongo3.rst index 67c82d85d..8da04f8da 100644 --- a/doc/migrate-to-pymongo3.rst +++ b/doc/migrate-to-pymongo3.rst @@ -405,6 +405,42 @@ can be replaced by this with PyMongo 2.9 or later: MongoClient ----------- +MongoClient connects asynchronously +................................... + +In PyMongo 3, the :class:`~pymongo.mongo_client.MongoClient` constructor no +longer blocks while connecting to the server or servers, and it no longer +raises :exc:`~pymongo.errors.ConnectionFailure` if they are unavailable, nor +:exc:`~pymongo.errors.ConfigurationError` if the user’s credentials are wrong. +Instead, the constructor returns immediately and launches the connection +process on background threads. The `connect` option is added to control whether +these threads are started immediately, or when the client is first used. + +For consistent behavior in PyMongo 2.x and PyMongo 3.x, code like this:: + + >>> from pymongo.errors import ConnectionFailure + >>> try: + ... client = MongoClient() + ... except ConnectionFailure: + ... print("Server not available") + >>> + +can be changed to this with PyMongo 2.9 or later: + +.. doctest:: + + >>> from pymongo.errors import ConnectionFailure + >>> client = MongoClient(connect=False) + >>> try: + ... result = client.admin.command("ismaster") + ... except ConnectionFailure: + ... print("Server not available") + >>> + +Any operation can be used to determine if the server is available. We choose +the "ismaster" command here because it is cheap and does not require auth, so +it is a simple way to check whether the server is available. + The max_pool_size parameter is removed ...................................... diff --git a/pymongo/common.py b/pymongo/common.py index 7c51540f0..b717464b6 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -385,7 +385,9 @@ VALIDATORS = { 'authmechanismproperties': validate_auth_mechanism_properties, 'uuidrepresentation': validate_uuid_representation, 'socketkeepalive': validate_boolean, - 'maxpoolsize': validate_positive_integer_or_none + 'maxpoolsize': validate_positive_integer_or_none, + 'connect': validate_boolean, + '_connect': validate_boolean } diff --git a/pymongo/connection.py b/pymongo/connection.py index 622c0944e..2259e873b 100644 --- a/pymongo/connection.py +++ b/pymongo/connection.py @@ -46,7 +46,7 @@ class Connection(MongoClient): def __init__(self, host=None, port=None, max_pool_size=None, network_timeout=None, document_class=dict, - tz_aware=False, _connect=True, **kwargs): + tz_aware=False, **kwargs): """Create a new connection to a single MongoDB instance at *host:port*. .. warning:: @@ -237,8 +237,8 @@ class Connection(MongoClient): kwargs['auto_start_request'] = kwargs.get('auto_start_request', True) kwargs['safe'] = kwargs.get('safe', False) - super(Connection, self).__init__(host, port, - max_pool_size, document_class, tz_aware, _connect, **kwargs) + super(Connection, self).__init__( + host, port, max_pool_size, document_class, tz_aware, **kwargs) def __repr__(self): if len(self.nodes) == 1: diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index d21dada9a..2a50c1586 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -92,8 +92,7 @@ class MongoClient(common.BaseObject): _rs_client = False def __init__(self, host=None, port=None, max_pool_size=100, - document_class=dict, tz_aware=False, _connect=True, - **kwargs): + document_class=dict, tz_aware=False, **kwargs): """Create a new connection to a single MongoDB instance at *host:port*. The resultant client object has connection-pooling built @@ -161,6 +160,8 @@ class MongoClient(common.BaseObject): - `use_greenlets`: If ``True``, :meth:`start_request()` will ensure that the current greenlet uses the same socket for all operations until :meth:`end_request()`. Defaults to ``False``. + - `connect`: if True (the default), immediately connect to MongoDB in + the foreground. Otherwise connect on the first operation. | **Write Concern options:** | (Only set if passed. No default values.) @@ -399,6 +400,7 @@ class MongoClient(common.BaseObject): "use read_preference instead.", DeprecationWarning, stacklevel=2) + _connect = options.get('_connect', options.get('connect', True)) if _connect: try: self._ensure_connected(True) diff --git a/pymongo/mongo_replica_set_client.py b/pymongo/mongo_replica_set_client.py index abcb291f8..afcdcd375 100644 --- a/pymongo/mongo_replica_set_client.py +++ b/pymongo/mongo_replica_set_client.py @@ -435,7 +435,7 @@ class MongoReplicaSetClient(common.BaseObject): _rs_client = True def __init__(self, hosts_or_uri=None, max_pool_size=100, - document_class=dict, tz_aware=False, _connect=True, **kwargs): + document_class=dict, tz_aware=False, **kwargs): """Create a new connection to a MongoDB replica set. The resultant client object has connection-pooling built @@ -514,6 +514,8 @@ class MongoReplicaSetClient(common.BaseObject): rather than thread-local, socket. Defaults to ``False``. `use_greenlets` with :class:`MongoReplicaSetClient` requires `Gevent `_ to be installed. + - `connect`: if True (the default), immediately connect to MongoDB + in the foreground. Otherwise connect on the first operation. | **Write Concern options:** | (Only set if passed. No default values.) @@ -715,6 +717,7 @@ class MongoReplicaSetClient(common.BaseObject): "use read_preference instead.", DeprecationWarning, stacklevel=2) + _connect = self.__opts.get('_connect', self.__opts.get('connect', True)) if _connect: try: self.refresh(initial=True) diff --git a/test/test_client.py b/test/test_client.py index 6f05d3532..4f692bee1 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -182,6 +182,15 @@ class TestClient(unittest.TestCase, TestRequestMixin): self.assertTrue(MongoClient(host, port)) + # Test that connect=False prevents the constructor from raising + # ConnectionFailure. + client = MongoClient("somedomainthatdoesnotexist.org", + connectTimeoutMS=100, connect=False) + try: + client.admin.command("ismaster") + except AutoReconnect: + pass + def test_equality(self): client = MongoClient(host, port) self.assertEqual(client, MongoClient(host, port)) diff --git a/test/test_replica_set_client.py b/test/test_replica_set_client.py index 548b4ce25..7467945b3 100644 --- a/test/test_replica_set_client.py +++ b/test/test_replica_set_client.py @@ -224,6 +224,16 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin): pair, replicaSet='fdlksjfdslkjfd') self.assertTrue(MongoReplicaSetClient(pair, replicaSet=self.name)) + # Test that connect=False prevents the constructor from raising + # ConnectionFailure. + client = MongoReplicaSetClient("somedomainthatdoesnotexist.org", + connectTimeoutMS=100, connect=False, + replicaSet=self.name) + try: + client.admin.command("ismaster") + except AutoReconnect: + pass + def test_repr(self): client = self._get_client()