Skip to content

Commit 6331539

Browse files
authored
MOTOR-1353 Update Synchro Tests to support PyMongo 4.9 (#298)
1 parent 972b7ff commit 6331539

File tree

9 files changed

+68
-49
lines changed

9 files changed

+68
-49
lines changed

doc/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ Changelog
33

44
.. currentmodule:: motor.motor_tornado
55

6+
Motor 3.6.0
7+
-----------
8+
- Add support for MongoDB 8.0 and PyMongo 4.9.
9+
- The length parameter in :meth:`MotorCursor.to_list` is now optional.
10+
611
Motor 3.5.1
712
-----------
813
- Fix runtime behavior of Motor generic class typing, e.g. ``client: AsyncIOMotorClient[Dict[str, Any]]``.

motor/core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ class AgnosticClient(AgnosticBaseProperties):
127127
server_info = AsyncRead()
128128
topology_description = ReadOnlyProperty()
129129
start_session = AsyncCommand(doc=docstrings.start_session_doc).wrap(ClientSession)
130+
_connect = AsyncRead()
130131

131132
def __init__(self, *args, **kwargs):
132133
"""Create a new connection to a single MongoDB instance at *host:port*.
@@ -1619,7 +1620,7 @@ def _each_got_more(self, callback, future):
16191620
self._framework.call_soon(self.get_io_loop(), functools.partial(callback, None, None))
16201621

16211622
@coroutine_annotation
1622-
def to_list(self, length):
1623+
def to_list(self, length=None):
16231624
"""Get a list of documents.
16241625
16251626
.. testsetup:: to_list

motor/core.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ class AgnosticBaseCursor(AgnosticBase, Generic[_DocumentType]):
697697
def next_object(self) -> Any: ...
698698
def each(self, callback: Callable) -> None: ...
699699
def _each_got_more(self, callback: Callable, future: Any) -> None: ...
700-
def to_list(self, length: Union[int, None]) -> Future[list]: ...
700+
def to_list(self, length: Optional[int] = ...) -> Future[list]: ...
701701
def _to_list(
702702
self, length: Union[int, None], the_list: list, future: Any, get_more_result: Any
703703
) -> None: ...

motor/docstrings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ async def add_3_to_x():
11491149
11501150
>>> async def f():
11511151
... cursor = collection.find().sort("_id", pymongo.DESCENDING)
1152-
... docs = await cursor.to_list(None)
1152+
... docs = await cursor.to_list()
11531153
... print([d["_id"] for d in docs])
11541154
...
11551155
>>> IOLoop.current().run_sync(f)
@@ -1163,7 +1163,7 @@ async def add_3_to_x():
11631163
... cursor = collection.find().sort(
11641164
... [("field1", pymongo.ASCENDING), ("field2", pymongo.DESCENDING)]
11651165
... )
1166-
... docs = await cursor.to_list(None)
1166+
... docs = await cursor.to_list()
11671167
... print([(d["field1"], d["field2"]) for d in docs])
11681168
...
11691169
>>> IOLoop.current().run_sync(f)

requirements/test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ tornado>=5
44
aiohttp>=3.8.7
55
motor[encryption]
66
cffi>=1.17.0rc1;python_version=="3.13"
7+
pytest_asyncio

synchro/__init__.py

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
# Make e.g. "from pymongo.errors import AutoReconnect" work. Note that
2929
# importing * won't pick up underscore-prefixed attrs.
3030
from gridfs import *
31-
from gridfs import _disallow_transactions
3231
from gridfs.errors import *
33-
from gridfs.grid_file import (
32+
from gridfs.synchronous.grid_file import (
3433
_SEEK_CUR,
3534
_SEEK_END,
3635
_UPLOAD_BUFFER_CHUNKS,
@@ -59,6 +58,7 @@
5958
server_type,
6059
srv_resolver,
6160
ssl_support,
61+
uri_parser,
6262
write_concern,
6363
)
6464

@@ -67,51 +67,50 @@
6767
from pymongo import _csot
6868
except ImportError:
6969
pass
70-
from pymongo import client_session
70+
from pymongo import client_session, synchronous
7171
from pymongo.auth import *
72-
from pymongo.auth import _build_credentials_tuple, _password_digest
73-
from pymongo.client_session import TransactionOptions, _TxnState
72+
from pymongo.client_session import TransactionOptions
7473
from pymongo.collation import *
7574
from pymongo.common import *
7675
from pymongo.common import _MAX_END_SESSIONS, _UUID_REPRESENTATIONS
7776
from pymongo.compression_support import _have_snappy, _have_zlib, _have_zstd
7877
from pymongo.cursor import *
79-
from pymongo.cursor import _QUERY_OPTIONS
78+
from pymongo.cursor_shared import _QUERY_OPTIONS
8079
from pymongo.encryption import *
81-
from pymongo.encryption import _MONGOCRYPTD_TIMEOUT_MS, _Encrypter
8280
from pymongo.encryption_options import *
8381
from pymongo.encryption_options import _HAVE_PYMONGOCRYPT
8482
from pymongo.errors import *
8583
from pymongo.event_loggers import *
86-
from pymongo.helpers import _check_command_response
84+
from pymongo.helpers_shared import _SENSITIVE_COMMANDS, _check_command_response
8785
from pymongo.lock import _create_lock
8886
from pymongo.message import (
8987
_COMMAND_OVERHEAD,
9088
_CursorAddress,
9189
_gen_find_command,
9290
_maybe_add_read_preference,
9391
)
94-
from pymongo.monitor import *
9592
from pymongo.monitoring import *
96-
from pymongo.monitoring import _LISTENERS, _SENSITIVE_COMMANDS, _Listeners
93+
from pymongo.monitoring import _LISTENERS, _Listeners
9794
from pymongo.ocsp_cache import _OCSPCache
9895
from pymongo.operations import *
99-
from pymongo.periodic_executor import *
100-
from pymongo.periodic_executor import _EXECUTORS
101-
from pymongo.pool import *
102-
from pymongo.pool import _METADATA, Connection, Pool, _PoolClosedError
10396
from pymongo.read_concern import *
10497
from pymongo.read_preferences import *
10598
from pymongo.read_preferences import _ServerMode
10699
from pymongo.results import *
107100
from pymongo.results import _WriteResult
108101
from pymongo.saslprep import *
109-
from pymongo.server import *
110102
from pymongo.server_selectors import *
111-
from pymongo.settings import *
112103
from pymongo.srv_resolver import _resolve, _SrvResolver
113104
from pymongo.ssl_support import *
114-
from pymongo.topology import *
105+
from pymongo.synchronous.client_session import _TxnState
106+
from pymongo.synchronous.monitor import *
107+
from pymongo.synchronous.periodic_executor import *
108+
from pymongo.synchronous.periodic_executor import _EXECUTORS
109+
from pymongo.synchronous.pool import *
110+
from pymongo.synchronous.pool import Connection, Pool, _PoolClosedError
111+
from pymongo.synchronous.server import *
112+
from pymongo.synchronous.settings import *
113+
from pymongo.synchronous.topology import *
115114
from pymongo.topology_description import *
116115
from pymongo.uri_parser import *
117116
from pymongo.uri_parser import _have_dnspython
@@ -131,6 +130,7 @@
131130

132131
_MotorRawBatchCursor = create_motor_class(_AgnosticRawBatchCursor)
133132
_MotorRawBatchCommandCursor = create_motor_class(_AgnosticRawBatchCommandCursor)
133+
get_hosts_and_min_ttl = _SrvResolver.get_hosts_and_min_ttl
134134

135135

136136
def wrap_synchro(fn):
@@ -262,9 +262,6 @@ class SynchroMeta(type):
262262
263263
- Motor methods which return Motor class instances are wrapped to return
264264
Synchro class instances.
265-
266-
- Certain internals accessed by PyMongo's unittests, such as _Cursor__data,
267-
are delegated from Synchro directly to PyMongo.
268265
"""
269266

270267
def __new__(cls, name, bases, attrs):
@@ -395,9 +392,10 @@ def __getitem__(self, name):
395392
return Database(self, name, delegate=self.delegate[name])
396393

397394
# For PyMongo tests that access client internals.
398-
_MongoClient__all_credentials = SynchroProperty()
399-
_MongoClient__kill_cursors_queue = SynchroProperty()
400-
_MongoClient__options = SynchroProperty()
395+
_connect = Sync()
396+
_all_credentials = SynchroProperty()
397+
_kill_cursors_queue = SynchroProperty()
398+
_options = SynchroProperty()
401399
_cache_credentials = SynchroProperty()
402400
_close_cursor_now = SynchroProperty()
403401
_get_topology = SynchroProperty()
@@ -577,6 +575,16 @@ class Cursor(Synchro):
577575
rewind = WrapOutgoing()
578576
clone = WrapOutgoing()
579577
close = Sync("close")
578+
to_list = Sync("to_list")
579+
580+
_query_flags = SynchroProperty()
581+
_data = SynchroProperty()
582+
_max_time_ms = SynchroProperty()
583+
_max_await_time_ms = SynchroProperty()
584+
_retrieved = SynchroProperty()
585+
_spec = SynchroProperty()
586+
_exhaust = SynchroProperty()
587+
_query_spec = SynchroProperty()
580588

581589
_next = Sync("next")
582590

@@ -618,22 +626,12 @@ def __exit__(self, exc_type, exc_val, exc_tb):
618626
# Don't suppress exceptions.
619627
return False
620628

621-
# For PyMongo tests that access cursor internals.
622-
_Cursor__data = SynchroProperty()
623-
_Cursor__exhaust = SynchroProperty()
624-
_Cursor__max_await_time_ms = SynchroProperty()
625-
_Cursor__max_time_ms = SynchroProperty()
626-
_Cursor__query_flags = SynchroProperty()
627-
_Cursor__query_spec = SynchroProperty()
628-
_Cursor__retrieved = SynchroProperty()
629-
_Cursor__spec = SynchroProperty()
630-
_read_preference = SynchroProperty()
631-
632629

633630
class CommandCursor(Cursor):
634631
__delegate_class__ = motor.motor_tornado.MotorCommandCursor
635632

636633
try_next = Sync("try_next")
634+
to_list = Sync("to_list")
637635

638636

639637
class GridOutCursor(Cursor):

synchro/synchrotest.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@
197197
"TestClient.test_handshake*",
198198
# This test is not a valid unittest target.
199199
"TestRangeQueryProse.run_test_cases",
200+
# The test logic interferes with the SynchroLoader.
201+
"ClientUnitTest.test_detected_environment_logging",
202+
"ClientUnitTest.test_detected_environment_warning",
203+
# The batch_size portion of this test does not work with synchro.
204+
"TestCursor.test_to_list_length",
205+
# These tests hang due to internal incompatibilities in to_list.
206+
"TestCursor.test_command_cursor_to_list*",
207+
# Motor does not allow calling to_list on a tailable cursor.
208+
"TestCursor.test_max_await_time_ms",
209+
"TestCursor.test_to_list_tailable",
200210
]
201211

202212

@@ -230,11 +240,7 @@ def find_spec(self, fullname, path, target=None):
230240
class SynchroModuleLoader(importlib.abc.Loader):
231241
def patch_spec(self, fullname):
232242
parts = fullname.split(".")
233-
if parts[-1] in ("gridfs", "pymongo"):
234-
# E.g. "import pymongo"
235-
return True
236-
elif len(parts) >= 2 and parts[-2] in ("gridfs", "pymongo"):
237-
# E.g. "import pymongo.mongo_client"
243+
if ("pymongo" in parts or "gridfs" in parts) and "asynchronous" not in parts:
238244
return True
239245

240246
return False
@@ -328,8 +334,17 @@ def want_method(method, classname):
328334
"pymongo.mongo_client",
329335
"pymongo.database",
330336
"pymongo.srv_resolver",
337+
"pymongo.synchronous.collection",
338+
"pymongo.synchronous.client_session",
339+
"pymongo.synchronous.command_cursor",
340+
"pymongo.synchronous.change_stream",
341+
"pymongo.synchronous.cursor",
342+
"pymongo.synchronous.encryption",
343+
"pymongo.synchronous.mongo_client",
344+
"pymongo.synchronous.database",
331345
"gridfs",
332346
"gridfs.grid_file",
347+
"gridfs.synchronous.grid_file",
333348
]:
334349
sys.modules.pop(n)
335350

@@ -341,7 +356,9 @@ def want_method(method, classname):
341356

342357
# Run the tests from the pymongo target dir with our custom plugin.
343358
os.chdir(sys.argv[1])
344-
code = pytest.main(sys.argv[2:] + ["-p", "no:warnings"], plugins=[SynchroPytestPlugin()])
359+
code = pytest.main(
360+
sys.argv[2:] + ["-m", "default"] + ["-p", "no:warnings"], plugins=[SynchroPytestPlugin()]
361+
)
345362

346363
if code != 0:
347364
sys.exit(code)

test/asyncio_tests/test_asyncio_cursor.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ def expected(start, stop):
192192
self.assertEqual(expected(100, 101), (await cursor.to_list(1)))
193193
self.assertEqual(expected(101, 102), (await cursor.to_list(1)))
194194
self.assertEqual(expected(102, 103), (await cursor.to_list(1)))
195-
self.assertEqual([], (await cursor.to_list(0)))
196195
self.assertEqual(expected(103, 105), (await cursor.to_list(2)))
197196

198197
# Only 95 docs left, make sure length=100 doesn't error or hang

tox.ini

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ allowlist_externals =
115115
setenv =
116116
PYTHONPATH = {envtmpdir}/mongo-python-driver
117117
commands =
118-
# TODO: Use "master" as part of MOTOR-1330
119-
git clone --depth 1 --branch v4.8 https://github.com/mongodb/mongo-python-driver.git {envtmpdir}/mongo-python-driver
118+
git clone --depth 1 https://github.com/mongodb/mongo-python-driver.git {envtmpdir}/mongo-python-driver
120119
python -m pip install -e {envtmpdir}/mongo-python-driver
121120
python -m synchro.synchrotest {envtmpdir}/mongo-python-driver -v {posargs}
122121

@@ -131,8 +130,7 @@ passenv =
131130
setenv =
132131
PYTHONPATH = {envtmpdir}/mongo-python-driver
133132
commands =
134-
# TODO: Use "master" as part of MOTOR-1330
135-
git clone --depth 1 --branch v4.8 https://github.com/mongodb/mongo-python-driver.git {envtmpdir}/mongo-python-driver
133+
git clone --depth 1 https://github.com/mongodb/mongo-python-driver.git {envtmpdir}/mongo-python-driver
136134
python -m pip install -e {envtmpdir}/mongo-python-driver
137135
python -m synchro.synchrotest {envtmpdir}/mongo-python-driver -v test/test_auth.py
138136

0 commit comments

Comments
 (0)