From 5283e7f6d9191b704195156e6cf2411993a7c548 Mon Sep 17 00:00:00 2001 From: Bastien Gerard Date: Wed, 19 Aug 2026 23:57:58 +0200 Subject: [PATCH] Drop support for pymongo 3 --- README.rst | 2 +- docs/changelog.rst | 14 +++++++ mongoengine/base/document.py | 16 +------- mongoengine/connection.py | 73 +++++++-------------------------- mongoengine/pymongo_support.py | 60 ++++++--------------------- setup.py | 2 +- tests/document/test_indexes.py | 49 +++------------------- tests/document/test_instance.py | 9 ++-- tests/queryset/test_geo.py | 64 +++++++---------------------- tests/test_connection.py | 51 ++++++++--------------- 10 files changed, 84 insertions(+), 256 deletions(-) diff --git a/README.rst b/README.rst index 3ea73d256..215423266 100644 --- a/README.rst +++ b/README.rst @@ -62,7 +62,7 @@ Dependencies ============ MongoEngine requires: -- PyMongo >=3.12,<5.0 +- PyMongo >=4.0 The following optional packages enable additional functionality: diff --git a/docs/changelog.rst b/docs/changelog.rst index ca9ad6c80..d70daa586 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -25,6 +25,20 @@ Changes in 1.0.0 - Log a warning in case users creates multiple Document classes with the same name as it can lead to unexpected behavior #1778 - Fix use of $geoNear or $collStats in aggregate #2493 - BREAKING CHANGE: Further to the deprecation warning, remove ability to use an unpacked list to `Queryset.aggregate(*pipeline)`, a plain list must be provided instead `Queryset.aggregate(pipeline)`, as it's closer to pymongo interface +- BREAKING CHANGE: PyMongo 3.x is no longer supported. + + As a consequence: + + - ``QuerySet.count()`` no longer supports queries using ``$near``, + ``$nearSphere``, ``$geoNear``, or ``$where``. PyMongo's + ``count_documents()`` rejects these operators and raises + ``OperationFailure``; the removed ``Cursor.count()`` fallback is no + longer available. Use ``$geoWithin`` with ``$center`` or + ``$centerSphere`` for countable geospatial filters, and ``$expr`` instead + of ``$where``. + - GeoHaystack index specifications using the ``)`` prefix are no longer + supported and raise ``NotImplementedError``. + - BREAKING CHANGE: Further to the deprecation warning, remove `full_response` from `QuerySet.modify` as it wasn't supported with Pymongo 3+ - Fixed stacklevel of many warnings (to point places emitting the warning more accurately) - Add support for collation/hint/comment to delete/update and aggregate #2842 diff --git a/mongoengine/base/document.py b/mongoengine/base/document.py index 400fb7931..3bb43c62e 100644 --- a/mongoengine/base/document.py +++ b/mongoengine/base/document.py @@ -30,11 +30,6 @@ NON_FIELD_ERRORS = "__all__" -try: - GEOHAYSTACK = pymongo.GEOHAYSTACK -except AttributeError: - GEOHAYSTACK = None - class BaseDocument: # TODO simplify how `_changed_fields` is used. @@ -931,10 +926,7 @@ def _build_index_spec(cls, spec): elif key.startswith("("): direction = pymongo.GEOSPHERE elif key.startswith(")"): - try: - direction = pymongo.GEOHAYSTACK - except AttributeError: - raise NotImplementedError + raise NotImplementedError("GeoHaystack indexes are not supported") elif key.startswith("*"): direction = pymongo.GEO2D if key.startswith(("+", "-", "*", "$", "#", "(", ")")): @@ -959,11 +951,7 @@ def _build_index_spec(cls, spec): index_list.append((key, direction)) # Don't add cls to a geo index - if ( - include_cls - and direction not in (pymongo.GEO2D, pymongo.GEOSPHERE) - and (GEOHAYSTACK is None or direction != GEOHAYSTACK) - ): + if include_cls and direction not in (pymongo.GEO2D, pymongo.GEOSPHERE): index_list.insert(0, ("_cls", 1)) if index_list: diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 4728cb377..984792613 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -10,14 +10,9 @@ except ImportError: from pymongo.database import _check_name -# DriverInfo was added in PyMongo 3.7. -try: - from pymongo.driver_info import DriverInfo -except ImportError: - DriverInfo = None +from pymongo.driver_info import DriverInfo import mongoengine -from mongoengine.pymongo_support import PYMONGO_VERSION __all__ = [ "DEFAULT_CONNECTION_NAME", @@ -165,19 +160,9 @@ def _get_connection_settings( ReadPreference.SECONDARY_PREFERRED, ) - # Starting with PyMongo v3.5, the "readpreference" option is - # returned as a string (e.g. "secondaryPreferred") and not an - # int (e.g. 3). - # TODO simplify the code below once we drop support for - # PyMongo v3.4. - read_pf_mode = normalized_uri_options["readpreference"] - if isinstance(read_pf_mode, str): - read_pf_mode = read_pf_mode.lower() + read_pf_mode = normalized_uri_options["readpreference"].lower() for preference in read_preferences: - if ( - preference.name.lower() == read_pf_mode - or preference.mode == read_pf_mode - ): + if preference.name.lower() == read_pf_mode: ReadPrefClass = preference.__class__ break @@ -213,7 +198,7 @@ def _get_connection_settings( if "uuidrepresentation" not in keys and "uuidrepresentation" not in conn_settings: warnings.warn( "No uuidRepresentation is specified! Falling back to " - "'pythonLegacy' which is the default for pymongo 3.x. " + "'pythonLegacy' for backward compatibility. " "For compatibility with other MongoDB drivers this should be " "specified as 'standard' or '{java,csharp}Legacy' to work with " "older drivers in those languages. This will be changed to " @@ -333,26 +318,14 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False): raise ConnectionFailure(msg) def _clean_settings(settings_dict): - if PYMONGO_VERSION < (4,): - irrelevant_fields_set = { - "name", - "username", - "password", - "authentication_source", - "authentication_mechanism", - "authmechanismproperties", - } - rename_fields = {} - else: - irrelevant_fields_set = {"name"} - rename_fields = { - "authentication_source": "authSource", - "authentication_mechanism": "authMechanism", - } + rename_fields = { + "authentication_source": "authSource", + "authentication_mechanism": "authMechanism", + } return { rename_fields.get(k, k): v for k, v in settings_dict.items() - if k not in irrelevant_fields_set and v is not None + if k != "name" and v is not None } raw_conn_settings = _connection_settings[alias].copy() @@ -361,10 +334,9 @@ def _clean_settings(settings_dict): # alias and remove the database name and authentication info (we don't # care about them at this point). conn_settings = _clean_settings(raw_conn_settings) - if DriverInfo is not None: - conn_settings.setdefault( - "driver", DriverInfo("MongoEngine", mongoengine.__version__) - ) + conn_settings.setdefault( + "driver", DriverInfo("MongoEngine", mongoengine.__version__) + ) # Determine if we should use PyMongo's or mongomock's MongoClient. if "mongo_client_class" in conn_settings: @@ -429,25 +401,8 @@ def get_db(alias=DEFAULT_CONNECTION_NAME, reconnect=False): if alias not in _dbs: conn = get_connection(alias) - conn_settings = _connection_settings[alias] - db = conn[conn_settings["name"]] - # Authenticate if necessary - if ( - PYMONGO_VERSION < (4,) - and conn_settings["username"] - and ( - conn_settings["password"] - or conn_settings["authentication_mechanism"] == "MONGODB-X509" - ) - and conn_settings["authmechanismproperties"] is None - ): - auth_kwargs = {"source": conn_settings["authentication_source"]} - if conn_settings["authentication_mechanism"] is not None: - auth_kwargs["mechanism"] = conn_settings["authentication_mechanism"] - db.authenticate( - conn_settings["username"], conn_settings["password"], **auth_kwargs - ) - _dbs[alias] = db + db_name = _connection_settings[alias]["name"] + _dbs[alias] = conn[db_name] return _dbs[alias] diff --git a/mongoengine/pymongo_support.py b/mongoengine/pymongo_support.py index 3c819610f..fe1baf7b2 100644 --- a/mongoengine/pymongo_support.py +++ b/mongoengine/pymongo_support.py @@ -4,26 +4,20 @@ import pymongo from bson import binary, json_util -from pymongo.errors import OperationFailure from mongoengine import connection PYMONGO_VERSION = tuple(pymongo.version_tuple[:2]) -# This will be changed to UuidRepresentation.UNSPECIFIED in a future -# (breaking) release. -if PYMONGO_VERSION >= (4,): - LEGACY_JSON_OPTIONS = json_util.LEGACY_JSON_OPTIONS.with_options( - uuid_representation=binary.UuidRepresentation.PYTHON_LEGACY, - ) -else: - LEGACY_JSON_OPTIONS = json_util.DEFAULT_JSON_OPTIONS +LEGACY_JSON_OPTIONS = json_util.LEGACY_JSON_OPTIONS.with_options( + uuid_representation=binary.UuidRepresentation.PYTHON_LEGACY, +) def count_documents( collection, filter, skip=None, limit=None, hint=None, collation=None ): - """Pymongo>3.7 deprecates count in favour of count_documents""" + """Count documents, using collection metadata when possible.""" if limit == 0: return 0 # Pymongo raises an OperationFailure if called with limit=0 @@ -37,47 +31,17 @@ def count_documents( if collation is not None: kwargs["collation"] = collation - # count_documents appeared in pymongo 3.7 - if PYMONGO_VERSION >= (3, 7): - try: - is_active_session = connection._get_session() is not None - if not filter and set(kwargs) <= {"max_time_ms"} and not is_active_session: - # when no filter is provided, estimated_document_count - # is a lot faster as it uses the collection metadata - return collection.estimated_document_count(**kwargs) - else: - return collection.count_documents( - filter=filter, session=connection._get_session(), **kwargs - ) - except OperationFailure as err: - if PYMONGO_VERSION >= (4,): - raise - - # OperationFailure - accounts for some operators that used to work - # with .count but are no longer working with count_documents (i.e $geoNear, $near, and $nearSphere) - # fallback to deprecated Cursor.count - # Keeping this should be reevaluated the day pymongo removes .count entirely - if ( - "$geoNear, $near, and $nearSphere are not allowed in this context" - not in str(err) - and "$where is not allowed in this context" not in str(err) - ): - raise - - cursor = collection.find(filter) - for option, option_value in kwargs.items(): - cursor_method = getattr(cursor, option) - cursor = cursor_method(option_value) - with_limit_and_skip = "skip" in kwargs or "limit" in kwargs - return cursor.count(with_limit_and_skip=with_limit_and_skip) + session = connection._get_session() + if not filter and not kwargs and session is None: + # when no filter is provided, estimated_document_count + # is a lot faster as it uses the collection metadata + return collection.estimated_document_count(**kwargs) + return collection.count_documents(filter=filter, session=session, **kwargs) def list_collection_names(db, include_system_collections=False): - """Pymongo>3.7 deprecates collection_names in favour of list_collection_names""" - if PYMONGO_VERSION >= (3, 7): - collections = db.list_collection_names(session=connection._get_session()) - else: - collections = db.collection_names(session=connection._get_session()) + """Return collection names, optionally including system collections.""" + collections = db.list_collection_names(session=connection._get_session()) if not include_system_collections: collections = [c for c in collections if not c.startswith("system.")] diff --git a/setup.py b/setup.py index 5865bb56f..f7f7021ce 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ def get_version(version_tuple): "Topic :: Software Development :: Libraries :: Python Modules", ] -install_require = ["pymongo>=3.12,<5.0"] +install_require = ["pymongo>=4.0,<5.0"] tests_require = [ "pytest", "pytest-cov", diff --git a/tests/document/test_indexes.py b/tests/document/test_indexes.py index 927bc7af6..d8db625ca 100644 --- a/tests/document/test_indexes.py +++ b/tests/document/test_indexes.py @@ -249,55 +249,18 @@ class Place(Document): info = [value["key"] for key, value in info.items()] assert [("location.point", "2dsphere")] in info - def test_explicit_geohaystack_index(self): - """Ensure that geohaystack indexes work when created via meta[indexes]""" - # This test can be removed when pymongo 3.x is no longer supported - if PYMONGO_VERSION >= (4,): - pytest.skip("GEOHAYSTACK has been removed in pymongo 4.0") - - class Place(Document): - location = DictField() - name = StringField() - meta = {"indexes": [(")location.point", "name")]} - - assert [ - {"fields": [("location.point", "geoHaystack"), ("name", 1)]} - ] == Place._meta["index_specs"] - - # GeoHaystack index creation is not supported for now from meta, as it - # requires a bucketSize parameter. - if False: - Place.ensure_indexes() - info = Place._get_collection().index_information() - info = [value["key"] for key, value in info.items()] - assert [("location.point", "geoHaystack")] in info - def test_create_geohaystack_index(self): - """Ensure that geohaystack indexes can be created""" + """Ensure that removed GeoHaystack indexes raise a clear error.""" class Place(Document): location = DictField() name = StringField() - if PYMONGO_VERSION >= (4,): - expected_error = NotImplementedError - elif get_mongodb_version() >= (4, 9): - expected_error = OperationFailure - else: - expected_error = None - - # This test can be removed when pymongo 3.x is no longer supported - if expected_error: - with pytest.raises(expected_error): - Place.create_index( - {"fields": (")location.point", "name")}, - bucketSize=10, - ) - else: - Place.create_index({"fields": (")location.point", "name")}, bucketSize=10) - info = Place._get_collection().index_information() - info = [value["key"] for key, value in info.items()] - assert [("location.point", "geoHaystack"), ("name", 1)] in info + with pytest.raises(NotImplementedError, match="GeoHaystack"): + Place.create_index( + {"fields": (")location.point", "name")}, + bucketSize=10, + ) def test_dictionary_indexes(self): """Ensure that indexes are used when meta[indexes] contains diff --git a/tests/document/test_instance.py b/tests/document/test_instance.py index 5428ad45f..a12330a9b 100644 --- a/tests/document/test_instance.py +++ b/tests/document/test_instance.py @@ -10,7 +10,7 @@ import bson import pytest from bson import DBRef, ObjectId -from pymongo.errors import DuplicateKeyError +from pymongo.errors import DuplicateKeyError, OperationFailure from mongoengine import * from mongoengine import signals @@ -3047,11 +3047,8 @@ def __str__(self): return this.name == '1' || this.name == '2';}"""}) assert [str(b) for b in custom_qs] == ["1", "2"] - - # count only will work with this raw query before pymongo 4.x, but - # the length is also implicitly checked above - if PYMONGO_VERSION < (4,): - assert custom_qs.count() == 2 + with pytest.raises(OperationFailure): + custom_qs.count() def test_switch_db_instance(self): register_connection("testdb-1", "mongoenginetest2") diff --git a/tests/queryset/test_geo.py b/tests/queryset/test_geo.py index e87d27aea..dbf9ca54f 100644 --- a/tests/queryset/test_geo.py +++ b/tests/queryset/test_geo.py @@ -1,8 +1,10 @@ import datetime import unittest +import pytest +from pymongo.errors import OperationFailure + from mongoengine import * -from mongoengine.pymongo_support import PYMONGO_VERSION from tests.utils import MongoDBTestCase @@ -40,6 +42,18 @@ def __unicode__(self): return event1, event2, event3 + def test_count__unsupported_geo_operator__raises_operation_failure(self): + self._create_event_data() + unsupported_queries = ( + {"location": {"$near": [-87.67892, 41.9120459]}}, + {"location": {"$nearSphere": [-87.67892, 41.9120459]}}, + {"$geoNear": {"near": [-87.67892, 41.9120459]}}, + ) + + for query in unsupported_queries: + with self.subTest(query=query), pytest.raises(OperationFailure): + self.Event.objects(__raw__=query).count() + def test_near(self): """Make sure the "near" operator works.""" event1, event2, event3 = self._create_event_data() @@ -48,15 +62,11 @@ def test_near(self): # note that "near" will show the san francisco event, too, # although it sorts to last. events = self.Event.objects(location__near=[-87.67892, 41.9120459]) - if PYMONGO_VERSION < (4,): - assert events.count() == 3 assert list(events) == [event1, event3, event2] # ensure ordering is respected by "near" events = self.Event.objects(location__near=[-87.67892, 41.9120459]) events = events.order_by("-date") - if PYMONGO_VERSION < (4,): - assert events.count() == 3 assert list(events) == [event3, event1, event2] def test_near_and_max_distance(self): @@ -68,8 +78,6 @@ def test_near_and_max_distance(self): # find events within 10 degrees of san francisco point = [-122.415579, 37.7566023] events = self.Event.objects(location__near=point, location__max_distance=10) - if PYMONGO_VERSION < (4,): - assert events.count() == 1 assert list(events) == [event2] def test_near_and_min_distance(self): @@ -81,8 +89,6 @@ def test_near_and_min_distance(self): # find events at least 10 degrees away of san francisco point = [-122.415579, 37.7566023] events = self.Event.objects(location__near=point, location__min_distance=10) - if PYMONGO_VERSION < (4,): - assert events.count() == 2 assert list(events) == [event3, event1] def test_within_distance(self): @@ -159,15 +165,11 @@ def test_2dsphere_near(self): # note that "near" will show the san francisco event, too, # although it sorts to last. events = self.Event.objects(location__near=[-87.67892, 41.9120459]) - if PYMONGO_VERSION < (4,): - assert events.count() == 3 assert list(events) == [event1, event3, event2] # ensure ordering is respected by "near" events = self.Event.objects(location__near=[-87.67892, 41.9120459]) events = events.order_by("-date") - if PYMONGO_VERSION < (4,): - assert events.count() == 3 assert list(events) == [event3, event1, event2] def test_2dsphere_near_and_max_distance(self): @@ -179,24 +181,18 @@ def test_2dsphere_near_and_max_distance(self): # find events within 10km of san francisco point = [-122.415579, 37.7566023] events = self.Event.objects(location__near=point, location__max_distance=10000) - if PYMONGO_VERSION < (4,): - assert events.count() == 1 assert list(events) == [event2] # find events within 1km of greenpoint, broolyn, nyc, ny events = self.Event.objects( location__near=[-73.9509714, 40.7237134], location__max_distance=1000 ) - if PYMONGO_VERSION < (4,): - assert events.count() == 0 assert list(events) == [] # ensure ordering is respected by "near" events = self.Event.objects( location__near=[-87.67892, 41.9120459], location__max_distance=10000 ).order_by("-date") - if PYMONGO_VERSION < (4,): - assert events.count() == 2 assert list(events) == [event3, event1] def test_2dsphere_geo_within_box(self): @@ -248,16 +244,12 @@ def test_2dsphere_near_and_min_max_distance(self): location__min_distance=1000, location__max_distance=10000, ).order_by("-date") - if PYMONGO_VERSION < (4,): - assert events.count() == 1 assert list(events) == [event3] # ensure ordering is respected by "near" with "min_distance" events = self.Event.objects( location__near=[-87.67892, 41.9120459], location__min_distance=10000 ).order_by("-date") - if PYMONGO_VERSION < (4,): - assert events.count() == 1 assert list(events) == [event2] def test_2dsphere_geo_within_center(self): @@ -303,8 +295,6 @@ class Event(Document): # note that "near" will show the san francisco event, too, # although it sorts to last. events = Event.objects(venue__location__near=[-87.67892, 41.9120459]) - if PYMONGO_VERSION < (4,): - assert events.count() == 3 assert list(events) == [event1, event3, event2] def test_geo_spatial_embedded(self): @@ -333,8 +323,6 @@ class Point(Document): # Finds both points because they are within 60 km of the reference # point equidistant between them. points = Point.objects(location__near_sphere=[-122, 37.5]) - if PYMONGO_VERSION < (4,): - assert points.count() == 2 assert list(points) == [north_point, south_point] # Same behavior for _within_spherical_distance @@ -346,8 +334,6 @@ class Point(Document): points = Point.objects( location__near_sphere=[-122, 37.5], location__max_distance=60 / earth_radius ) - if PYMONGO_VERSION < (4,): - assert points.count() == 2 assert list(points) == [north_point, south_point] # Test query works with max_distance, being farer from one point @@ -355,16 +341,12 @@ class Point(Document): location__near_sphere=[-122, 37.8], location__max_distance=60 / earth_radius ) close_point = points.first() - if PYMONGO_VERSION < (4,): - assert points.count() == 1 assert list(points) == [north_point] # Test query works with min_distance, being farer from one point points = Point.objects( location__near_sphere=[-122, 37.8], location__min_distance=60 / earth_radius ) - if PYMONGO_VERSION < (4,): - assert points.count() == 1 far_point = points.first() assert list(points) == [south_point] assert close_point != far_point @@ -372,15 +354,11 @@ class Point(Document): # Finds both points, but orders the north point first because it's # closer to the reference point to the north. points = Point.objects(location__near_sphere=[-122, 38.5]) - if PYMONGO_VERSION < (4,): - assert points.count() == 2 assert list(points) == [north_point, south_point] # Finds both points, but orders the south point first because it's # closer to the reference point to the south. points = Point.objects(location__near_sphere=[-122, 36.5]) - if PYMONGO_VERSION < (4,): - assert points.count() == 2 assert list(points) == [south_point, north_point] # Finds only one point because only the first point is within 60km of @@ -404,18 +382,12 @@ class Road(Document): # near point = {"type": "Point", "coordinates": [40, 5]} roads = Road.objects.filter(line__near=point["coordinates"]) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] roads = Road.objects.filter(line__near=point) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] roads = Road.objects.filter(line__near={"$geometry": point}) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] # Within @@ -478,18 +450,12 @@ class Road(Document): # near point = {"type": "Point", "coordinates": [40, 5]} roads = Road.objects.filter(poly__near=point["coordinates"]) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] roads = Road.objects.filter(poly__near=point) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] roads = Road.objects.filter(poly__near={"$geometry": point}) - if PYMONGO_VERSION < (4,): - assert roads.count() == 1 assert list(roads) == [road] # Within diff --git a/tests/test_connection.py b/tests/test_connection.py index b41ef5826..0b83afa8d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -32,7 +32,6 @@ get_connection, get_db, ) -from mongoengine.pymongo_support import PYMONGO_VERSION def random_str(): @@ -188,17 +187,11 @@ def test___get_connection_settings(self): funky_host = "mongodb://root:12345678@1.1.1.1:27017,2.2.2.2:27017,3.3.3.3:27017/db_api?replicaSet=s0&readPreference=secondary&uuidRepresentation=javaLegacy&readPreferenceTags=region:us-west-2,usage:api" settings = _get_connection_settings(host=funky_host) - if PYMONGO_VERSION < (4,): - read_pref = Secondary( - tag_sets=[{"region": "us-west-2", "usage": "api"}], - max_staleness=-1, - ) - else: - read_pref = Secondary( - tag_sets=[{"region": "us-west-2", "usage": "api"}], - max_staleness=-1, - hedge=None, - ) + read_pref = Secondary( + tag_sets=[{"region": "us-west-2", "usage": "api"}], + max_staleness=-1, + hedge=None, + ) assert settings == { "authentication_mechanism": None, "authentication_source": None, @@ -347,17 +340,15 @@ def test_disconnect_does_not_close_client_used_by_another_alias(self): client2.admin.command("ping") disconnect("disconnect_reused_client_test_2") # The client is now closed: - if PYMONGO_VERSION >= (4,): - with pytest.raises(InvalidOperation): - client2.admin.command("ping") + with pytest.raises(InvalidOperation): + client2.admin.command("ping") # 3rd client connected to the same cluster with different options # is not closed either. client3.admin.command("ping") disconnect("disconnect_reused_client_test_3") # 3rd client is now closed: - if PYMONGO_VERSION >= (4,): - with pytest.raises(InvalidOperation): - client3.admin.command("ping") + with pytest.raises(InvalidOperation): + client3.admin.command("ping") def test_disconnect_all(self): connections = mongoengine.connection._connections @@ -483,14 +474,10 @@ def test_uri_without_credentials_doesnt_override_conn_settings(self): # OperationFailure means that mongoengine attempted authentication # w/ the provided username/password and failed - that's the desired # behavior. If the MongoDB URI would override the credentials - if PYMONGO_VERSION >= (4,): - with pytest.raises(OperationFailure): - db = get_db() - # pymongo 4.x does not call db.authenticate and needs to perform an operation to trigger the failure - db.list_collection_names() - else: - with pytest.raises(OperationFailure): - get_db() + with pytest.raises(OperationFailure): + db = get_db() + # Authentication is lazy; perform an operation to trigger the failure. + db.list_collection_names() def test_connect_uri_with_authsource(self): """Ensure that the connect() method works well with `authSource` @@ -567,10 +554,7 @@ def test_connection_pool_via_kwarg(self): conn = connect( "mongoenginetest", alias="max_pool_size_via_kwarg", **pool_size_kwargs ) - if PYMONGO_VERSION >= (4,): - assert conn.options.pool_options.max_pool_size == 100 - else: - assert conn.max_pool_size == 100 + assert conn.options.pool_options.max_pool_size == 100 def test_connection_pool_via_uri(self): """Ensure we can specify a max connection pool size using @@ -580,10 +564,7 @@ def test_connection_pool_via_uri(self): host="mongodb://localhost/test?maxpoolsize=100", alias="max_pool_size_via_uri", ) - if PYMONGO_VERSION >= (4,): - assert conn.options.pool_options.max_pool_size == 100 - else: - assert conn.max_pool_size == 100 + assert conn.options.pool_options.max_pool_size == 100 def test_write_concern(self): """Ensure write concern can be specified in connect() via @@ -656,7 +637,7 @@ def test_multiple_connection_settings(self): assert "t1" in mongo_connections.keys() assert "t2" in mongo_connections.keys() - # Handle PyMongo 3+ Async Connection (lazily established) + # Handle PyMongo's lazily established connection. # Ensure we are connected, throws ServerSelectionTimeoutError otherwise. # Purposely not catching exception to fail test if thrown. mongo_connections["t1"].server_info()