From b0fa8d8db3ad53791648d45cedcacb80f54f41ab Mon Sep 17 00:00:00 2001 From: RJX_from_pc <1058960881@qq.com> Date: Fri, 7 Jul 2017 12:54:28 +0800 Subject: [PATCH 1/8] contributor;change set function to set literal append contributor in CONTRIBUTING; change more efficient set constrution to set literal --- AUTHORS | 1 + mongoengine/connection.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1d724718c..96a7850ee 100644 --- a/AUTHORS +++ b/AUTHORS @@ -243,3 +243,4 @@ that much better: * Victor Varvaryuk * Stanislav Kaledin (https://github.com/sallyruthstruik) * Dmitry Yantsen (https://github.com/mrTable) + * Renjianxin (https://github.com/Davidrjx) diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 7eae810f1..34ff4dc31 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -146,13 +146,14 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False): raise MongoEngineConnectionError(msg) def _clean_settings(settings_dict): - irrelevant_fields = set([ - 'name', 'username', 'password', 'authentication_source', - 'authentication_mechanism' - ]) + # set literal more efficient than calling set function + irrelevant_fields_set = { + 'name', 'username', 'password', + 'authentication_source', 'authentication_mechanism' + } return { k: v for k, v in settings_dict.items() - if k not in irrelevant_fields + if k not in irrelevant_fields_set } # Retrieve a copy of the connection settings associated with the requested From 4a868dc3aca670334bbd206f18535b0d5ad9169c Mon Sep 17 00:00:00 2001 From: RJX_from_pc <1058960881@qq.com> Date: Fri, 18 Aug 2017 17:45:23 +0800 Subject: [PATCH 2/8] merger rjxdev into master and resolve merge conflict in AUTHORS --- AUTHORS | 2 ++ docs/guide/querying.rst | 9 ++++++++ mongoengine/queryset/transform.py | 18 ++++++++++++++-- tests/document/instance.py | 34 ++++++++++++++++++++++++++++++ tests/queryset/modify.py | 35 ++++++++++++++++++++++++++++++- tests/queryset/queryset.py | 26 +++++++++++++++++++++++ 6 files changed, 121 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 96a7850ee..1300180b2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -244,3 +244,5 @@ that much better: * Stanislav Kaledin (https://github.com/sallyruthstruik) * Dmitry Yantsen (https://github.com/mrTable) * Renjianxin (https://github.com/Davidrjx) + * Erdenezul Batmunkh (https://github.com/erdenezul) + diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 0bb196581..f1594dd29 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -565,6 +565,15 @@ cannot use the `$` syntax in keyword arguments it has been mapped to `S`:: >>> post.tags ['database', 'mongodb'] +From MongoDB version 2.6, push operator supports $position value which allows +to push values with index. + >>> post = BlogPost(title="Test", tags=["mongo"]) + >>> post.save() + >>> post.update(push__tags__0=["database", "code"]) + >>> post.reload() + >>> post.tags + ['database', 'code', 'mongo'] + .. note:: Currently only top level lists are handled, future versions of mongodb / pymongo plan to support nested positional operators. See `The $ positional diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index bb04ee376..a9907adac 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -284,7 +284,9 @@ def update(_doc_cls=None, **update): if isinstance(field, GeoJsonBaseField): value = field.to_mongo(value) - if op in (None, 'set', 'push', 'pull'): + if op == 'push' and isinstance(value, (list, tuple, set)): + value = [field.prepare_query_value(op, v) for v in value] + elif op in (None, 'set', 'push', 'pull'): if field.required or value is not None: value = field.prepare_query_value(op, value) elif op in ('pushAll', 'pullAll'): @@ -333,10 +335,22 @@ def update(_doc_cls=None, **update): value = {key: value} elif op == 'addToSet' and isinstance(value, list): value = {key: {'$each': value}} + elif op == 'push': + if parts[-1].isdigit(): + key = parts[0] + position = int(parts[-1]) + # $position expects an iterable. If pushing a single value, + # wrap it in a list. + if not isinstance(value, (set, tuple, list)): + value = [value] + value = {key: {'$each': value, '$position': position}} + elif isinstance(value, list): + value = {key: {'$each': value}} + else: + value = {key: value} else: value = {key: value} key = '$' + op - if key not in mongo_update: mongo_update[key] = value elif key in mongo_update and isinstance(mongo_update[key], dict): diff --git a/tests/document/instance.py b/tests/document/instance.py index 37bbe3373..609bc9000 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -22,6 +22,8 @@ from mongoengine.context_managers import switch_db, query_counter from mongoengine import signals +from tests.utils import needs_mongodb_v26 + TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), '../fields/mongoengine.png') @@ -826,6 +828,22 @@ def test_modify_update(self): self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())]) + @needs_mongodb_v26 + def test_modify_with_positional_push(self): + class BlogPost(Document): + tags = ListField(StringField()) + + post = BlogPost.objects.create(tags=['python']) + self.assertEqual(post.tags, ['python']) + post.modify(push__tags__0=['code', 'mongo']) + self.assertEqual(post.tags, ['code', 'mongo', 'python']) + + # Assert same order of the list items is maintained in the db + self.assertEqual( + BlogPost._get_collection().find_one({'_id': post.pk})['tags'], + ['code', 'mongo', 'python'] + ) + def test_save(self): """Ensure that a document may be saved in the database.""" @@ -3149,6 +3167,22 @@ class Person(Document): person.update(set__height=2.0) + @needs_mongodb_v26 + def test_push_with_position(self): + """Ensure that push with position works properly for an instance.""" + class BlogPost(Document): + slug = StringField() + tags = ListField(StringField()) + + blog = BlogPost() + blog.slug = "ABC" + blog.tags = ["python"] + blog.save() + + blog.update(push__tags__0=["mongodb", "code"]) + blog.reload() + self.assertEqual(blog.tags, ['mongodb', 'code', 'python']) + if __name__ == '__main__': unittest.main() diff --git a/tests/queryset/modify.py b/tests/queryset/modify.py index 607937f68..b37f9b734 100644 --- a/tests/queryset/modify.py +++ b/tests/queryset/modify.py @@ -1,6 +1,8 @@ import unittest -from mongoengine import connect, Document, IntField +from mongoengine import connect, Document, IntField, StringField, ListField + +from tests.utils import needs_mongodb_v26 __all__ = ("FindAndModifyTest",) @@ -94,6 +96,37 @@ def test_modify_with_fields(self): self.assertEqual(old_doc.to_mongo(), {"_id": 1}) self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}]) + @needs_mongodb_v26 + def test_modify_with_push(self): + class BlogPost(Document): + tags = ListField(StringField()) + + BlogPost.drop_collection() + + blog = BlogPost.objects.create() + + # Push a new tag via modify with new=False (default). + BlogPost(id=blog.id).modify(push__tags='code') + self.assertEqual(blog.tags, []) + blog.reload() + self.assertEqual(blog.tags, ['code']) + + # Push a new tag via modify with new=True. + blog = BlogPost.objects(id=blog.id).modify(push__tags='java', new=True) + self.assertEqual(blog.tags, ['code', 'java']) + + # Push a new tag with a positional argument. + blog = BlogPost.objects(id=blog.id).modify( + push__tags__0='python', + new=True) + self.assertEqual(blog.tags, ['python', 'code', 'java']) + + # Push multiple new tags with a positional argument. + blog = BlogPost.objects(id=blog.id).modify( + push__tags__1=['go', 'rust'], + new=True) + self.assertEqual(blog.tags, ['python', 'go', 'rust', 'code', 'java']) + if __name__ == '__main__': unittest.main() diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index d97b307d7..c78ed9857 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1903,6 +1903,32 @@ class BlogPost(Document): BlogPost.drop_collection() + @needs_mongodb_v26 + def test_update_push_with_position(self): + """Ensure that the 'push' update with position works properly. + """ + class BlogPost(Document): + slug = StringField() + tags = ListField(StringField()) + + BlogPost.drop_collection() + + post = BlogPost.objects.create(slug="test") + + BlogPost.objects.filter(id=post.id).update(push__tags="code") + BlogPost.objects.filter(id=post.id).update(push__tags__0=["mongodb", "python"]) + post.reload() + self.assertEqual(post.tags, ['mongodb', 'python', 'code']) + + BlogPost.objects.filter(id=post.id).update(set__tags__2="java") + post.reload() + self.assertEqual(post.tags, ['mongodb', 'python', 'java']) + + #test push with singular value + BlogPost.objects.filter(id=post.id).update(push__tags__0='scala') + post.reload() + self.assertEqual(post.tags, ['scala', 'mongodb', 'python', 'java']) + def test_update_push_and_pull_add_to_set(self): """Ensure that the 'pull' update operation works correctly. """ From ed287cef291cddeb7f563be85d21219149c78246 Mon Sep 17 00:00:00 2001 From: RJX_from_pc <1058960881@qq.com> Date: Fri, 18 Aug 2017 17:53:02 +0800 Subject: [PATCH 3/8] line ending character change --- AUTHORS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1300180b2..4eac5eb2d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -244,5 +244,4 @@ that much better: * Stanislav Kaledin (https://github.com/sallyruthstruik) * Dmitry Yantsen (https://github.com/mrTable) * Renjianxin (https://github.com/Davidrjx) - * Erdenezul Batmunkh (https://github.com/erdenezul) - + * Erdenezul Batmunkh (https://github.com/erdenezul) \ No newline at end of file From 53ef986dc5a310558c32453dede9fceb3ced8a2a Mon Sep 17 00:00:00 2001 From: Ren Jianxin Date: Wed, 4 Oct 2017 21:46:16 +0800 Subject: [PATCH 4/8] new test package connections and move related modules --- tests/connections/__init__.py | 0 tests/{ => connections}/test_connection.py | 104 ++++++++++++------ .../connections/test_replicaset_connection.py | 85 ++++++++++++++ tests/test_replicaset_connection.py | 51 --------- 4 files changed, 157 insertions(+), 83 deletions(-) create mode 100644 tests/connections/__init__.py rename tests/{ => connections}/test_connection.py (82%) create mode 100644 tests/connections/test_replicaset_connection.py delete mode 100644 tests/test_replicaset_connection.py diff --git a/tests/connections/__init__.py b/tests/connections/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_connection.py b/tests/connections/test_connection.py similarity index 82% rename from tests/test_connection.py rename to tests/connections/test_connection.py index cdcf13773..b8cc972d6 100644 --- a/tests/test_connection.py +++ b/tests/connections/test_connection.py @@ -211,6 +211,78 @@ def test_uri_without_credentials_doesnt_override_conn_settings(self): # behavior. If the MongoDB URI would override the credentials self.assertRaises(OperationFailure, get_db) + @unittest.skip('require authentication enabled and meet needed prerequisites') + def test_uri_overide_kwargs_with_right_authSource_not_pass(self): + """Authenticated during building connection""" + ###as prerequisite, require two users at the time when authentication is disabled### + #no_auth_conn_obj = connect(db='mongoenginetest') + #no_auth_conn_obj.admin.system.users.remove({}) + #no_auth_conn_obj.admin.add_user('rjxu', 'rjxp', read_only=True) + #no_auth_conn_obj.mongoenginetest.add_user('rjxtu','rjxtp', read_only=True) + + auth_conn_obj = connect( + db='mongoenginetest', + alias='authtest', + username='rjxu', + password='rjxp', + host="mongodb://rjxtu:rjxtp@localhost:27017" + + "/?authSource=mongoenginetest", + authentication_source='admin', + authentication_mechanism='MONGODB-X509' + ) + + if not IS_PYMONGO_3: + #attempt to read data from a collection authored by admin + def get_data_case(): + auth_db_obj = auth_conn_obj['admin'] + coll_from_auth_db = auth_db_obj['system.users'] + return [sys_user for sys_user in coll_from_auth_db.find()] + try: + get_data_case() + except: + self.assertRaises(OperationFailure, get_data_case) + else: + credentials_obj = auth_conn_obj._MongoClient__options.credentials + self.assertEqual(credentials_obj.username, 'rjxtu') + self.assertEqual(credentials_obj.source, 'mongoenginetest') + self.assertEqual(credentials_obj.mechanism, 'DEFAULT') + + @unittest.skip('require authentication enabled and meet needed prerequisite') + def test_post_auth_mongo_client_with_get_db(self): + '''Post authentication at get db stage''' + ###as prerequisite, require one user at the time when authentication is disabled### + #no_auth_conn_obj = connect(db='mongoenginetest') + #no_auth_conn_obj.admin.system.users.remove({}) + #no_auth_conn_obj.mongoenginetest.add_user('rjxtu', 'rjxtp', read_only=True) + + no_auth_conn_obj = connect( + db='mongoenginetest', + username='rjxtu', + password='rjxtp', + host="mongodb://localhost:27017", + authentication_source='mongoenginetest', + authentication_mechanism='DEFAULT' + ) + + #read data from auth or non-auth conn + def get_data_case(is_get_db=False): + if not is_get_db: + _db_obj = no_auth_conn_obj['mongoenginetest'] + else: + _db_obj = get_db() + coll_from_db = _db_obj['testcoll'] + return [_td for _td in coll_from_db.find()] + + # + try: + get_data_case() + except: + self.assertRaises(OperationFailure, get_data_case) + # + self.assertRaises(AssertionError, self.assertRaises, + OperationFailure, get_data_case, True) + + def test_connect_uri_with_authsource(self): """Ensure that the connect() method works well with `authSource` option in the URI. @@ -319,38 +391,6 @@ def test_write_concern(self): self.assertEqual(dict(conn1.write_concern), {'w': 1, 'j': True}) self.assertEqual(dict(conn2.write_concern), {'w': 1, 'j': True}) - def test_connect_with_replicaset_via_uri(self): - """Ensure connect() works when specifying a replicaSet via the - MongoDB URI. - """ - if IS_PYMONGO_3: - c = connect(host='mongodb://localhost/test?replicaSet=local-rs') - db = get_db() - self.assertTrue(isinstance(db, pymongo.database.Database)) - self.assertEqual(db.name, 'test') - else: - # PyMongo < v3.x raises an exception: - # "localhost:27017 is not a member of replica set local-rs" - with self.assertRaises(MongoEngineConnectionError): - c = connect(host='mongodb://localhost/test?replicaSet=local-rs') - - def test_connect_with_replicaset_via_kwargs(self): - """Ensure connect() works when specifying a replicaSet via the - connection kwargs - """ - if IS_PYMONGO_3: - c = connect(replicaset='local-rs') - self.assertEqual(c._MongoClient__options.replica_set_name, - 'local-rs') - db = get_db() - self.assertTrue(isinstance(db, pymongo.database.Database)) - self.assertEqual(db.name, 'test') - else: - # PyMongo < v3.x raises an exception: - # "localhost:27017 is not a member of replica set local-rs" - with self.assertRaises(MongoEngineConnectionError): - c = connect(replicaset='local-rs') - def test_datetime(self): connect('mongoenginetest', tz_aware=True) d = datetime.datetime(2010, 5, 5, tzinfo=utc) diff --git a/tests/connections/test_replicaset_connection.py b/tests/connections/test_replicaset_connection.py new file mode 100644 index 000000000..a72bb0113 --- /dev/null +++ b/tests/connections/test_replicaset_connection.py @@ -0,0 +1,85 @@ +import unittest + +from pymongo import ReadPreference + +from mongoengine.python_support import IS_PYMONGO_3 + +if IS_PYMONGO_3: + from pymongo import MongoClient + CONN_CLASS = MongoClient + READ_PREF = ReadPreference.SECONDARY +else: + from pymongo import ReplicaSetConnection + CONN_CLASS = ReplicaSetConnection + READ_PREF = ReadPreference.SECONDARY_ONLY + +import pymongo +import mongoengine +from mongoengine import * +from mongoengine.connection import (MongoEngineConnectionError, + get_db) + + +class ConnectionTest(unittest.TestCase): + + def setUp(self): + mongoengine.connection._connection_settings = {} + mongoengine.connection._connections = {} + mongoengine.connection._dbs = {} + + def tearDown(self): + mongoengine.connection._connection_settings = {} + mongoengine.connection._connections = {} + mongoengine.connection._dbs = {} + + def test_replicaset_uri_passes_read_preference(self): + """Requires a replica set called "rs" on port 27017 + """ + + try: + conn = connect(db='mongoenginetest', + host="mongodb://localhost/mongoenginetest?replicaSet=local-rs", + read_preference=READ_PREF) + except MongoEngineConnectionError as e: + return + + if not isinstance(conn, CONN_CLASS): + # really??? + return + + self.assertEqual(conn.read_preference, READ_PREF) + + def test_connect_with_replicaset_via_kwargs(self): + """Ensure connect() works when specifying a replicaSet via the + connection kwargs + """ + if IS_PYMONGO_3: + c = connect(replicaset='local-rs') + self.assertEqual(c._MongoClient__options.replica_set_name, + 'local-rs') + db = get_db() + self.assertTrue(isinstance(db, pymongo.database.Database)) + self.assertEqual(db.name, 'test') + else: + # PyMongo < v3.x raises an exception: + # "localhost:27017 is not a member of replica set local-rs" + with self.assertRaises(MongoEngineConnectionError): + c = connect(replicaset='local-rs') + + def test_connect_with_replicaset_via_uri(self): + """Ensure connect() works when specifying a replicaSet via the + MongoDB URI. + """ + if IS_PYMONGO_3: + c = connect(host='mongodb://localhost/test?replicaSet=local-rs') + db = get_db() + self.assertTrue(isinstance(db, pymongo.database.Database)) + self.assertEqual(db.name, 'test') + else: + # PyMongo < v3.x raises an exception: + # "localhost:27017 is not a member of replica set local-rs" + with self.assertRaises(MongoEngineConnectionError): + c = connect(host='mongodb://localhost/test?replicaSet=local-rs') + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_replicaset_connection.py b/tests/test_replicaset_connection.py deleted file mode 100644 index a53f5903e..000000000 --- a/tests/test_replicaset_connection.py +++ /dev/null @@ -1,51 +0,0 @@ -import unittest - -from pymongo import ReadPreference - -from mongoengine.python_support import IS_PYMONGO_3 - -if IS_PYMONGO_3: - from pymongo import MongoClient - CONN_CLASS = MongoClient - READ_PREF = ReadPreference.SECONDARY -else: - from pymongo import ReplicaSetConnection - CONN_CLASS = ReplicaSetConnection - READ_PREF = ReadPreference.SECONDARY_ONLY - -import mongoengine -from mongoengine import * -from mongoengine.connection import MongoEngineConnectionError - - -class ConnectionTest(unittest.TestCase): - - def setUp(self): - mongoengine.connection._connection_settings = {} - mongoengine.connection._connections = {} - mongoengine.connection._dbs = {} - - def tearDown(self): - mongoengine.connection._connection_settings = {} - mongoengine.connection._connections = {} - mongoengine.connection._dbs = {} - - def test_replicaset_uri_passes_read_preference(self): - """Requires a replica set called "rs" on port 27017 - """ - - try: - conn = connect(db='mongoenginetest', - host="mongodb://localhost/mongoenginetest?replicaSet=rs", - read_preference=READ_PREF) - except MongoEngineConnectionError as e: - return - - if not isinstance(conn, CONN_CLASS): - # really??? - return - - self.assertEqual(conn.read_preference, READ_PREF) - -if __name__ == '__main__': - unittest.main() From c610c4c54326413210273182178b40b10e8bcf43 Mon Sep 17 00:00:00 2001 From: Ren Jianxin Date: Sat, 7 Oct 2017 21:22:30 +0800 Subject: [PATCH 5/8] related with replica set connection involving replica set connection config tuning and related tests --- mongoengine/connection.py | 35 ++++++----- .../connections/test_replicaset_connection.py | 62 ++++++++++++++++++- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 34ff4dc31..7ffa1c03c 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -92,17 +92,21 @@ def register_connection(alias, name=None, host=None, port=None, if uri_dict.get('database'): conn_settings['name'] = uri_dict.get('database') - for param in ('read_preference', 'username', 'password'): + for param in ('username', 'password'): if uri_dict.get(param): conn_settings[param] = uri_dict[param] uri_options = uri_dict['options'] - if 'replicaset' in uri_options: - conn_settings['replicaSet'] = uri_options['replicaset'] + if 'replicaset' in uri_options or \ + 'replicaset' in [kw_key.lower() for kw_key in kwargs.keys()]: + conn_settings['isreplicaSet'] = True if 'authsource' in uri_options: conn_settings['authentication_source'] = uri_options['authsource'] if 'authmechanism' in uri_options: conn_settings['authentication_mechanism'] = uri_options['authmechanism'] + if IS_PYMONGO_3 and 'readpreference' in uri_options and \ + 'readpreferencetags' in uri_options: + del conn_settings['read_preference'] else: resolved_hosts.append(entity) conn_settings['host'] = resolved_hosts @@ -176,19 +180,18 @@ def _clean_settings(settings_dict): # For replica set connections with PyMongo 2.x, use # MongoReplicaSetClient. # TODO remove this once we stop supporting PyMongo 2.x. - if 'replicaSet' in conn_settings and not IS_PYMONGO_3: - connection_class = MongoReplicaSetClient - conn_settings['hosts_or_uri'] = conn_settings.pop('host', None) - - # hosts_or_uri has to be a string, so if 'host' was provided - # as a list, join its parts and separate them by ',' - if isinstance(conn_settings['hosts_or_uri'], list): - conn_settings['hosts_or_uri'] = ','.join( - conn_settings['hosts_or_uri']) - - # Discard port since it can't be used on MongoReplicaSetClient - conn_settings.pop('port', None) - + if conn_settings.has_key('isreplicaSet'): + if not IS_PYMONGO_3: + connection_class = MongoReplicaSetClient + conn_settings['hosts_or_uri'] = conn_settings.pop('host', None) + # hosts_or_uri has to be a string, so if 'host' was provided + # as a list, join its parts and separate them by ',' + if isinstance(conn_settings['hosts_or_uri'], list): + conn_settings['hosts_or_uri'] = ','.join( + conn_settings['hosts_or_uri']) + # Discard port since it can't be used on MongoReplicaSetClient + conn_settings.pop('port', None) + del conn_settings['isreplicaSet'] # Iterate over all of the connection settings and if a connection with # the same parameters is already established, use it instead of creating # a new one. diff --git a/tests/connections/test_replicaset_connection.py b/tests/connections/test_replicaset_connection.py index a72bb0113..6b6678ca3 100644 --- a/tests/connections/test_replicaset_connection.py +++ b/tests/connections/test_replicaset_connection.py @@ -1,6 +1,7 @@ import unittest -from pymongo import ReadPreference +from pymongo import (ReadPreference, + read_preferences) from mongoengine.python_support import IS_PYMONGO_3 @@ -81,5 +82,64 @@ def test_connect_with_replicaset_via_uri(self): with self.assertRaises(MongoEngineConnectionError): c = connect(host='mongodb://localhost/test?replicaSet=local-rs') + def test_read_preference_from_replica_set_in_uri_as_host(self): + '''read preference from replica set cluster''' + #test case about uri option overrides kwargs and read_preference with tag sets + if IS_PYMONGO_3: + conn_obj = connect( + db='testrjx', + host="mongodb://localhost:27017/?replicaset=dev_rs" + + "&readpreference=nearest&readpreferencetags=", + read_preference=ReadPreference.SECONDARY + ) + read_preference_obj = conn_obj.read_preference + self.assertEqual(read_preference_obj.mode, ReadPreference.NEAREST.mode) + self.assertEqual(read_preference_obj.tag_sets, [{}]) + else: + if pymongo.version_tuple[1] < 9: # like v2.8 + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs" + + "&read_preference=nearest&readpreferencetags=dc:east,use:dev", + read_preference=ReadPreference.SECONDARY, + readpreferencetags=[{}] #tag_sets as an alternative + ) + self.assertEqual(c_obj.read_preference, ReadPreference.NEAREST) + self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) + else: + #for 2.9 as transition version, also accept read_preference object subclassing _ServerMode + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs", + read_preference=read_preferences.SecondaryPreferred( + [{'dc':'east','use':'dev'}] + ), + ) + self.assertEqual(c_obj.read_preference, ReadPreference.SECONDARY_PREFERRED) + self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) + + def test_replica_set_as_uri_or_kwargs(self): + '''replicaset as uri option or kwargs''' + # dev_rs as real replica set name, while test_rs is false as comparision + # replicaset case insensitive + if not IS_PYMONGO_3: #like 2.9 and 2.8 + print pymongo.version_tuple + # uri opt overrides kwargs for connecting replica set + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs", + replicaset='test_rs' + ) + self.assertEqual(c_obj._MongoReplicaSetClient__name, 'dev_rs') + else: #like 3.3 + #here, kwargs overrides uri option + #though wrong replica set to be connected, it is ok until issuing real data operation + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27017/?replicaSet=dev_rs", + replicaSet='test_rs' + ) + self.assertEqual(c_obj._MongoClient__options.replica_set_name, 'test_rs') + if __name__ == '__main__': unittest.main() From 05fe6ef1b8472ae7e78b22ef4af6e85632b60a69 Mon Sep 17 00:00:00 2001 From: Ren Jianxin Date: Wed, 11 Oct 2017 13:22:15 +0800 Subject: [PATCH 6/8] extra print delete print statement --- tests/connections/test_replicaset_connection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/connections/test_replicaset_connection.py b/tests/connections/test_replicaset_connection.py index 6b6678ca3..25890eb0e 100644 --- a/tests/connections/test_replicaset_connection.py +++ b/tests/connections/test_replicaset_connection.py @@ -123,7 +123,6 @@ def test_replica_set_as_uri_or_kwargs(self): # dev_rs as real replica set name, while test_rs is false as comparision # replicaset case insensitive if not IS_PYMONGO_3: #like 2.9 and 2.8 - print pymongo.version_tuple # uri opt overrides kwargs for connecting replica set c_obj = connect( db='testrjx', From 4ddf14d55ada8a2596d77de3b71cd8252701b741 Mon Sep 17 00:00:00 2001 From: Ren Jianxin Date: Wed, 11 Oct 2017 13:57:24 +0800 Subject: [PATCH 7/8] modifying replica set tests tuning test methods under py2 --- .../connections/test_replicaset_connection.py | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/tests/connections/test_replicaset_connection.py b/tests/connections/test_replicaset_connection.py index 25890eb0e..62e707e2e 100644 --- a/tests/connections/test_replicaset_connection.py +++ b/tests/connections/test_replicaset_connection.py @@ -97,26 +97,28 @@ def test_read_preference_from_replica_set_in_uri_as_host(self): self.assertEqual(read_preference_obj.tag_sets, [{}]) else: if pymongo.version_tuple[1] < 9: # like v2.8 - c_obj = connect( - db='testrjx', - host="mongodb://localhost:27018/?replicaset=dev_rs" - + "&read_preference=nearest&readpreferencetags=dc:east,use:dev", - read_preference=ReadPreference.SECONDARY, - readpreferencetags=[{}] #tag_sets as an alternative - ) - self.assertEqual(c_obj.read_preference, ReadPreference.NEAREST) - self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) + with self.assertRaises(MongoEngineConnectionError): + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs" + + "&read_preference=nearest&readpreferencetags=dc:east,use:dev", + read_preference=ReadPreference.SECONDARY, + readpreferencetags=[{}] #tag_sets as an alternative + ) + self.assertEqual(c_obj.read_preference, ReadPreference.NEAREST) + self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) else: #for 2.9 as transition version, also accept read_preference object subclassing _ServerMode - c_obj = connect( - db='testrjx', - host="mongodb://localhost:27018/?replicaset=dev_rs", - read_preference=read_preferences.SecondaryPreferred( - [{'dc':'east','use':'dev'}] - ), - ) - self.assertEqual(c_obj.read_preference, ReadPreference.SECONDARY_PREFERRED) - self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) + with self.assertRaises(MongoEngineConnectionError): + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs", + read_preference=read_preferences.SecondaryPreferred( + [{'dc':'east','use':'dev'}] + ), + ) + self.assertEqual(c_obj.read_preference, ReadPreference.SECONDARY_PREFERRED) + self.assertEqual(c_obj.tag_sets, [{'dc':'east','use':'dev'}]) def test_replica_set_as_uri_or_kwargs(self): '''replicaset as uri option or kwargs''' @@ -124,12 +126,13 @@ def test_replica_set_as_uri_or_kwargs(self): # replicaset case insensitive if not IS_PYMONGO_3: #like 2.9 and 2.8 # uri opt overrides kwargs for connecting replica set - c_obj = connect( - db='testrjx', - host="mongodb://localhost:27018/?replicaset=dev_rs", - replicaset='test_rs' - ) - self.assertEqual(c_obj._MongoReplicaSetClient__name, 'dev_rs') + with self.assertRaises(MongoEngineConnectionError): + c_obj = connect( + db='testrjx', + host="mongodb://localhost:27018/?replicaset=dev_rs", + replicaset='test_rs' + ) + self.assertEqual(c_obj._MongoReplicaSetClient__name, 'dev_rs') else: #like 3.3 #here, kwargs overrides uri option #though wrong replica set to be connected, it is ok until issuing real data operation From 22f1fb8528f177db62f340d442b429f1b4a79fff Mon Sep 17 00:00:00 2001 From: Ren Jianxin Date: Wed, 11 Oct 2017 16:02:46 +0800 Subject: [PATCH 8/8] code review follow flake8 --- mongoengine/connection.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 7ffa1c03c..dd35fd2dc 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -97,15 +97,15 @@ def register_connection(alias, name=None, host=None, port=None, conn_settings[param] = uri_dict[param] uri_options = uri_dict['options'] - if 'replicaset' in uri_options or \ - 'replicaset' in [kw_key.lower() for kw_key in kwargs.keys()]: + if 'replicaset' in uri_options \ + or 'replicaset' in [kw_key.lower() for kw_key in kwargs.keys()]: conn_settings['isreplicaSet'] = True if 'authsource' in uri_options: conn_settings['authentication_source'] = uri_options['authsource'] if 'authmechanism' in uri_options: conn_settings['authentication_mechanism'] = uri_options['authmechanism'] - if IS_PYMONGO_3 and 'readpreference' in uri_options and \ - 'readpreferencetags' in uri_options: + if IS_PYMONGO_3 and 'readpreference' in uri_options \ + and 'readpreferencetags' in uri_options: del conn_settings['read_preference'] else: resolved_hosts.append(entity) @@ -180,7 +180,7 @@ def _clean_settings(settings_dict): # For replica set connections with PyMongo 2.x, use # MongoReplicaSetClient. # TODO remove this once we stop supporting PyMongo 2.x. - if conn_settings.has_key('isreplicaSet'): + if 'isreplicaSet' in conn_settings.keys(): if not IS_PYMONGO_3: connection_class = MongoReplicaSetClient conn_settings['hosts_or_uri'] = conn_settings.pop('host', None)