diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index f9b61d73e5..3f0379bf19 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -100,7 +100,7 @@ jobs: make_sdist: name: Make SDist - runs-on: macos-13 + runs-on: macos-14 steps: - uses: actions/checkout@v5 with: diff --git a/doc/changelog.rst b/doc/changelog.rst index bde6125c44..a9917d211e 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,6 +1,22 @@ Changelog ========= +Changes in Version 4.15.4 (2025/11/11) +-------------------------------------- + +Version 4.15.4 is a bug fix release. + +- Relaxed the callback type of :meth:`~pymongo.asynchronous.client_session.AsyncClientSession.with_transaction` to allow the broader Awaitable type rather than only Coroutine objects. +- Added the missing Python 3.14 trove classifier to the package metadata. + +Issues Resolved +............... + +See the `PyMongo 4.15.4 release notes in JIRA`_ for the list of resolved issues +in this release. + +.. _PyMongo 4.15.4 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=47237 + Changes in Version 4.15.3 (2025/10/07) -------------------------------------- diff --git a/doc/index.rst b/doc/index.rst index 85812d1b14..17b12e51a8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -22,7 +22,7 @@ work with MongoDB from Python. Getting Help ------------ If you're having trouble or have questions about PyMongo, ask your question on -our `MongoDB Community Forum `_. +one of the platforms listed on `Technical Support `_. You may also want to consider a `commercial support subscription `_. Once you get an answer, it'd be great if you could work it back into this @@ -37,7 +37,7 @@ project. Feature Requests / Feedback --------------------------- -Use our `feedback engine `_ +Use our `feedback engine `_ to send us feature requests and general feedback about PyMongo. Contributing diff --git a/pymongo/asynchronous/client_session.py b/pymongo/asynchronous/client_session.py index be02295cea..ce2d7667f5 100644 --- a/pymongo/asynchronous/client_session.py +++ b/pymongo/asynchronous/client_session.py @@ -143,8 +143,8 @@ TYPE_CHECKING, Any, AsyncContextManager, + Awaitable, Callable, - Coroutine, Mapping, MutableMapping, NoReturn, @@ -600,7 +600,7 @@ def _inherit_option(self, name: str, val: _T) -> _T: async def with_transaction( self, - callback: Callable[[AsyncClientSession], Coroutine[Any, Any, _T]], + callback: Callable[[AsyncClientSession], Awaitable[_T]], read_concern: Optional[ReadConcern] = None, write_concern: Optional[WriteConcern] = None, read_preference: Optional[_ServerMode] = None, diff --git a/test/asynchronous/test_transactions.py b/test/asynchronous/test_transactions.py index 5c2a4f6fae..6849da9896 100644 --- a/test/asynchronous/test_transactions.py +++ b/test/asynchronous/test_transactions.py @@ -15,6 +15,7 @@ """Execute Transactions Spec tests.""" from __future__ import annotations +import asyncio import sys from io import BytesIO from test.asynchronous.utils_spec_runner import AsyncSpecRunner @@ -469,6 +470,17 @@ async def callback2(session): async with self.client.start_session() as s: self.assertEqual(await s.with_transaction(callback2), "Foo") + @async_client_context.require_transactions + @async_client_context.require_async + async def test_callback_awaitable_no_coroutine(self): + def callback(_): + future = asyncio.Future() + future.set_result("Foo") + return future + + async with self.client.start_session() as s: + self.assertEqual(await s.with_transaction(callback), "Foo") + @async_client_context.require_transactions async def test_callback_not_retried_after_timeout(self): listener = OvertCommandListener() diff --git a/test/test_transactions.py b/test/test_transactions.py index f4578deddb..0c5bd42d7d 100644 --- a/test/test_transactions.py +++ b/test/test_transactions.py @@ -15,6 +15,7 @@ """Execute Transactions Spec tests.""" from __future__ import annotations +import asyncio import sys from io import BytesIO from test.utils_spec_runner import SpecRunner @@ -461,6 +462,17 @@ def callback2(session): with self.client.start_session() as s: self.assertEqual(s.with_transaction(callback2), "Foo") + @client_context.require_transactions + @client_context.require_async + def test_callback_awaitable_no_coroutine(self): + def callback(_): + future = asyncio.Future() + future.set_result("Foo") + return future + + with self.client.start_session() as s: + self.assertEqual(s.with_transaction(callback), "Foo") + @client_context.require_transactions def test_callback_not_retried_after_timeout(self): listener = OvertCommandListener() diff --git a/tools/synchro.py b/tools/synchro.py index e502f96281..17dac3f21f 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -322,6 +322,14 @@ def translate_coroutine_types(lines: list[str]) -> list[str]: index = lines.index(type) new = type.replace(old, res.group(3)) lines[index] = new + coroutine_types = [line for line in lines if "Awaitable[" in line] + for type in coroutine_types: + res = re.search(r"Awaitable\[([A-z]+)\]", type) + if res: + old = res[0] + index = lines.index(type) + new = type.replace(old, res.group(1)) + lines[index] = new return lines