Skip to content

Commit

Permalink
MOTOR-1169 Update PyMongo Deps and Remove Workarounds (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
blink1073 committed Aug 24, 2023
1 parent f905df8 commit 5348e5a
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 112 deletions.
16 changes: 16 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ Changelog

.. currentmodule:: motor.motor_tornado

Motor 3.3.0
-----------

- Add support for PyMongo 4.4+.
- Add support for Python 3.12.
- Add inline type hints for public APIs.
- Added new helper methods for Atlas Search Index (requires MongoDB Server 7.0+):
:meth:`~motor.motor_tornado.MotorCollection.list_search_indexes`,
:meth:`~motor.motor_tornado.MotorCollection.create_search_index`,
:meth:`~motor.motor_tornado.MotorCollection.create_search_indexes`,
:meth:`~motor.motor_tornado.MotorCollection.drop_search_index`,
:meth:`~motor.motor_tornado.MotorCollection.update_search_index`
- Added :meth:`~motor.motor_tornado.MotorDatabase.cursor_command`
and :meth:`~motor.motor_tornado.MotorCommandCursor.try_next` to support
executing an arbitrary command that returns a cursor.

Motor 3.2.0
-----------

Expand Down
74 changes: 42 additions & 32 deletions doc/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Requirements
The current version of Motor requires:

* CPython 3.7 and later.
* PyMongo_ 4.4 and later.
* PyMongo_ 4.5 and later.

Motor can integrate with either Tornado or asyncio.

Expand Down Expand Up @@ -53,6 +53,8 @@ Motor and PyMongo
+-------------------+-----------------+
| 3.2 | 4.4+ |
+-------------------+-----------------+
| 3.3 | 4.5+ |
+-------------------+-----------------+

Motor and MongoDB
`````````````````
Expand Down Expand Up @@ -88,6 +90,8 @@ Motor and MongoDB
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 |**N**|**N**|**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 |**N**|**N**|**N**|**N**|**N**|**N**| Y | Y | Y | Y | Y | Y | Y |
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

There is no relationship between PyMongo and MongoDB version numbers, although
the numbers happen to be close or equal in recent releases of PyMongo and MongoDB.
Expand Down Expand Up @@ -130,6 +134,8 @@ known to be incompatible, or have not been tested together.
+---------------+-----+-----+-----+-----+-----+
| | 3.2 |**N**|**N**|**N**| Y |
+---------------+-----+-----+-----+-----+-----+
| | 3.3 |**N**|**N**|**N**| Y |
+---------------+-----+-----+-----+-----+-----+

Motor and Python
````````````````
Expand All @@ -149,37 +155,41 @@ Motor 3.0 dropped support for Pythons older than 3.7.

Motor 3.1.1 added support for Python 3.11.

+-------------------------------------------------------------------------------------------+
| Python Version |
+=====================+=====+=====+=====+=======+=======+=====+=====+=====+=====+=====+=====+
| | 2.7 | 3.3 | 3.4 | 3.5.0 | 3.5.2 | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| Motor Version | 1.0 | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 1.1 | Y | Y | Y | Y | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 1.2 | Y |**N**| Y |**N** | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 1.3 | Y |**N**| Y |**N** | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.0 | Y |**N**| Y |**N** | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.1 | Y |**N**| Y |**N** | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.2 |**N**|**N**|**N**|**N** | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.3 |**N**|**N**|**N**|**N** | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.4 |**N**|**N**|**N**|**N** | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 2.5 |**N**|**N**|**N**|**N** | Y | Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 3.0 |**N**|**N**|**N**|**N** |**N** |**N**| Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 3.1 |**N**|**N**|**N**|**N** |**N** |**N**| Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
| | 3.2 |**N**|**N**|**N**|**N** |**N** |**N**| Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-------+-------+-----+-----+-----+-----+-----+-----+
Motor 3.3 added support for Python 3.12.

+---------------------------------------------------------------+
| Python Version |
+=====================+=====+=====+=====+=====+=====+=====+=====+
| | 3.6 | 3.7 | 3.8 | 3.9 | 3.10| 3.11| 3.12|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| Motor Version | 1.0 | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.1 | Y |**N**|**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.2 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 1.3 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.0 | Y | Y |**N**|**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.1 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.2 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.3 | Y | Y | Y |**N**|**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.4 | Y | Y | Y | Y |**N**|**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 2.5 | Y | Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.0 |**N**| Y | Y | Y | Y |**N**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.1 |**N**| Y | Y | Y | Y |**Y**|**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.2 |**N**| Y | Y | Y | Y | Y |**N**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+
| | 3.3 |**N**| Y | Y | Y | Y | Y |**Y**|
+---------------+-----+-----+-----+-----+-----+-----+-----+-----+

Not Supported
-------------
Expand Down
2 changes: 1 addition & 1 deletion motor/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"""Version-related data for motor."""

version_tuple = (3, 3, 0, ".dev0")
version_tuple = (3, 3, 0)


def get_version_string() -> str:
Expand Down
35 changes: 9 additions & 26 deletions motor/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ def watch(
show_expanded_events,
)

async def _cursor_command(
async def cursor_command(
self,
command,
value=1,
Expand Down Expand Up @@ -804,10 +804,6 @@ def inner():

return cursor_class(cursor, self)

# TODO: MOTOR-1169
if hasattr(Database, "cursor_command"):
cursor_command = _cursor_command

@property
def client(self):
"""This MotorDatabase's :class:`MotorClient`."""
Expand Down Expand Up @@ -876,11 +872,14 @@ class AgnosticCollection(AgnosticBaseProperties):
count_documents = AsyncRead()
create_index = AsyncCommand(doc=docstrings.create_index_doc)
create_indexes = AsyncCommand(doc=docstrings.create_indexes_doc)
create_search_index = AsyncCommand()
create_search_indexes = AsyncCommand()
delete_many = AsyncCommand(doc=docstrings.delete_many_doc)
delete_one = AsyncCommand(doc=docstrings.delete_one_doc)
distinct = AsyncRead()
drop = AsyncCommand(doc=docstrings.drop_doc)
drop_index = AsyncCommand()
drop_search_index = AsyncCommand()
drop_indexes = AsyncCommand()
estimated_document_count = AsyncCommand()
find_one = AsyncRead(doc=docstrings.find_one_doc)
Expand All @@ -897,20 +896,13 @@ class AgnosticCollection(AgnosticBaseProperties):
replace_one = AsyncCommand(doc=docstrings.replace_one_doc)
update_many = AsyncCommand(doc=docstrings.update_many_doc)
update_one = AsyncCommand(doc=docstrings.update_one_doc)

update_search_index = AsyncCommand()
with_options = DelegateMethod().wrap(Collection)

# TODO: MOTOR-1169
if hasattr(Collection, "create_search_index"):
create_search_index = AsyncCommand()
create_search_indexes = AsyncCommand()
drop_search_index = AsyncCommand()
update_search_index = AsyncCommand()
_async_list_search_indexes = AsyncRead(attr_name="list_search_indexes")

_async_aggregate = AsyncRead(attr_name="aggregate")
_async_aggregate_raw_batches = AsyncRead(attr_name="aggregate_raw_batches")
_async_list_indexes = AsyncRead(attr_name="list_indexes")
_async_list_search_indexes = AsyncRead(attr_name="list_search_indexes")

def __init__(
self,
Expand Down Expand Up @@ -1325,7 +1317,7 @@ async def print_indexes():
# Latent cursor that will send initial command on first "async for".
return cursor_class(self, self._async_list_indexes, session=session, **kwargs)

def _list_search_indexes(self, *args, **kwargs):
def list_search_indexes(self, *args, **kwargs):
"""Return a cursor over search indexes for the current collection."""
cursor_class = create_class_with_framework(
AgnosticLatentCommandCursor, self._framework, self.__module__
Expand All @@ -1334,10 +1326,6 @@ def _list_search_indexes(self, *args, **kwargs):
# Latent cursor that will send initial command on first "async for".
return cursor_class(self, self._async_list_search_indexes, *args, **kwargs)

# TODO: MOTOR-1169
if hasattr(Collection, "list_search_indexes"):
list_search_indexes = _list_search_indexes

def wrap(self, obj):
if obj.__class__ is Collection:
# Replace pymongo.collection.Collection with MotorCollection.
Expand Down Expand Up @@ -1784,7 +1772,7 @@ class AgnosticCommandCursor(AgnosticBaseCursor):

_CommandCursor__die = AsyncRead()

async def _try_next(self):
async def try_next(self):
"""Advance the cursor without blocking indefinitely.
This method returns the next document without waiting
Expand All @@ -1806,10 +1794,6 @@ def inner():
loop = self.get_io_loop()
return await self._framework.run_on_executor(loop, inner)

# TODO: MOTOR-1169
if hasattr(CommandCursor, "try_next"):
try_next = _try_next

def _query_flags(self):
return 0

Expand Down Expand Up @@ -2109,6 +2093,7 @@ class AgnosticClientEncryption(AgnosticBase):

create_data_key = AsyncCommand(doc=docstrings.create_data_key_doc)
encrypt = AsyncCommand()
encrypt_expression = AsyncCommand()
decrypt = AsyncCommand()
close = AsyncCommand(doc=docstrings.close_doc)

Expand All @@ -2119,8 +2104,6 @@ class AgnosticClientEncryption(AgnosticBase):
add_key_alt_name = AsyncCommand()
get_key_by_alt_name = AsyncCommand()
remove_key_alt_name = AsyncCommand()
if hasattr(ClientEncryption, "encrypt_expression"):
encrypt_expression = AsyncCommand()

def __init__(
self,
Expand Down
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,33 +43,33 @@ classifiers = [
"Programming Language :: Python :: 3.12",
]
dependencies = [
"pymongo>=4.4,<5",
"pymongo>=4.5,<5",
]

[project.optional-dependencies]
aws = [
"pymongo[aws]>=4.4,<5",
"pymongo[aws]>=4.5,<5",
]
encryption = [
"pymongo[encryption]>=4.4,<5",
"pymongo[encryption]>=4.5,<5",
]
gssapi = [
"pymongo[gssapi]>=4.4,<5",
"pymongo[gssapi]>=4.5,<5",
]
ocsp = [
"pymongo[ocsp]>=4.4,<5",
"pymongo[ocsp]>=4.5,<5",
]
snappy = [
"pymongo[snappy]>=4.4,<5",
"pymongo[snappy]>=4.5,<5",
]
srv = [
"pymongo[srv]>=4.4,<5",
"pymongo[srv]>=4.5,<5",
]
test = [
"pytest>=7", "mockupdb", "tornado>=5", "aiohttp", "motor[encryption]"
]
zstd = [
"pymongo[zstd]>=4.4,<5",
"pymongo[zstd]>=4.5,<5",
]

[project.urls]
Expand Down
13 changes: 3 additions & 10 deletions test/asyncio_tests/test_asyncio_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,9 @@ async def test_reconnect_in_case_connection_closed_by_mongo(self):
# lost, as result we should have AutoReconnect instead of
# IncompleteReadError
pool = get_primary_pool(cx)

# TODO: MOTOR-1169
if hasattr(pool, "conns"):
conn = pool.conns.pop()
conn.conn.close()
pool.conns.appendleft(conn)
else:
socket = pool.sockets.pop()
socket.sock.close()
pool.sockets.appendleft(socket)
conn = pool.conns.pop()
conn.conn.close()
pool.conns.appendleft(conn)

with self.assertRaises(pymongo.errors.AutoReconnect):
await cx.motor_test.test_collection.find_one()
Expand Down
4 changes: 0 additions & 4 deletions test/asyncio_tests/test_asyncio_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,6 @@ def test_sub_collection(self):
@env.require_no_standalone
@asyncio_test
async def test_async_create_encrypted_collection(self):
if pymongo.version_tuple < (4, 4, 0):
raise unittest.SkipTest("Requires PyMongo 4.4+")
c = self.collection
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
Expand All @@ -291,8 +289,6 @@ async def test_async_create_encrypted_collection(self):

@asyncio_test
async def test_async_encrypt_expression(self):
if pymongo.version_tuple < (4, 4, 0):
raise unittest.SkipTest("Requires PyMongo 4.4+")
c = self.collection
KMS_PROVIDERS = {"local": {"key": b"\x00" * 96}}
self.cx.drop_database("db")
Expand Down
6 changes: 1 addition & 5 deletions test/asyncio_tests/test_asyncio_cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,8 @@ async def test_exhaust(self):
# Ensure a pool.
await client.db.collection.find_one()

# TODO: MOTOR-1169
pool = get_primary_pool(client)
if hasattr(pool, "conns"):
conns = pool.conns
else:
conns = pool.sockets
conns = pool.conns

# Make sure the socket is returned after exhaustion.
cur = client[self.db.name].test.find(cursor_type=CursorType.EXHAUST)
Expand Down
4 changes: 1 addition & 3 deletions test/asyncio_tests/test_asyncio_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""Test AsyncIOMotorDatabase."""

import unittest
from test import SkipTest, env
from test import env
from test.asyncio_tests import AsyncIOTestCase, asyncio_test

import pymongo.database
Expand Down Expand Up @@ -159,8 +159,6 @@ def test_with_options(self):
@asyncio_test
async def test_cursor_command(self):
db = self.db
if not hasattr(pymongo.database.Database, "cursor_command"):
raise SkipTest("MOTOR-1169")
await db.test.drop()

docs = [{"_id": i, "doc": i} for i in range(3)]
Expand Down

0 comments on commit 5348e5a

Please sign in to comment.