Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

lpulley
Copy link

@lpulley lpulley commented Oct 16, 2025

Summary

AsyncClientSession.with_transaction's callback is typed as Callable[[AsyncClientSession], Coroutine[Any, Any, _T]] but does not use any of Coroutine's interface that isn't already provided in its parent, Awaitable.

In other words: at runtime, any function that returns an Awaitable works as callback, but callback's type unnecessarily requires that the function's return type matches Coroutine.

The type should be changed from Callable[[AsyncClientSession], Coroutine[Any, Any, _T]] to Callable[[AsyncClientSession], Awaitable[_T]] so that a non-Coroutine Awaitable can be used as callback.

Changes in this PR

Changed the type of AsyncClientSession.with_transaction's callback argument from Callable[[AsyncClientSession], Coroutine[Any, Any, _T]] to Callable[[AsyncClientSession], Awaitable[_T]].

Testing Plan

Type-checking should cover this change.

Checklist

Checklist for Author

  • Did you update the changelog (if necessary)?
  • Is the intention of the code captured in relevant tests?
  • If there are new TODOs, has a related JIRA ticket been created?

Checklist for Reviewer {@primary_reviewer}

  • Does the title of the PR reference a JIRA Ticket?
  • Do you fully understand the implementation? (Would you be comfortable explaining how this code works to someone else?)
  • Have you checked for spelling & grammar errors?
  • Is all relevant documentation (README or docstring) updated?

@lpulley lpulley marked this pull request as ready for review October 16, 2025 17:30
@lpulley lpulley requested a review from a team as a code owner October 16, 2025 17:30
@lpulley lpulley requested a review from sleepyStick October 16, 2025 17:30
@sleepyStick sleepyStick changed the title Loosen AsyncClientSession.with_transaction callback type PYTHON-5623: Loosen AsyncClientSession.with_transaction callback type Oct 16, 2025
@ShaneHarvey
Copy link
Member

Thanks. For clarity, could you provide a code example that incorrectly fails type checking before this change?

@sleepyStick
Copy link
Contributor

Hey! Thanks for your work on this! I acknowledge the need for this change.
Currently this repos sync vs async code is managed via synchro and the synchro script (located in tools/synchro.py) isn't stripping Awaitable for sync. Thus, a change to the synchro script is required for this PR to be merged.
If you feel inclined to modify the synchro script, that would be super helpful but otherwise myself, or someone else on the team, will get to it.

@lpulley
Copy link
Author

lpulley commented Oct 16, 2025

For clarity, could you provide a code example that incorrectly fails type checking before this change?

We're using Trio, and for compatibility with asyncio libraries like pymongo.asynchronous, there exists trio_asyncio which provides aio_as_trio(...) and trio_as_aio(...). They wrap async callables, async iterators, or async context managers in "proxy" objects (Asyncio_Trio_Wrapper and Trio_Asyncio_Wrapper, respectively), and those proxy objects are Awaitable but not Coroutine.

So, I want to use lambda session: trio_as_aio(my_trio_async_function)(session) as my callback function; it is Callable[..., Awaitable[...]] but not Callable[..., Coroutine[...]], and it shouldn't need to be.

Of course, I can imagine other scenarios where someone may have a callback that returns something that is Awaitable but not Coroutine, but this is mine.


Currently this repos sync vs async code is managed via synchro and the synchro script (located in tools/synchro.py) isn't stripping Awaitable for sync. Thus, a change to the synchro script is required for this PR to be merged.

Ah... I see. Well, the gist of this is that x: Callable[..., Awaitable[T]] is more suitable than x: Callable[..., Coroutine[Any, Any, T]] if all you need to do is await x(...)!

If you feel inclined to modify the synchro script, that would be super helpful but otherwise myself, or someone else on the team, will get to it.

I'd be happy to, assuming it's a simple change, but I'm not sure exactly how it works. If this is simple for you to do, then by all means, go for it! We'll use a type: ignore in the meantime.

@cj81499
Copy link

cj81499 commented Oct 17, 2025

Is the required change to tools/synchro.py simply adding and calling translate_awaitable_types, similar to the existing translate_coroutine_types? If it's more involved than that, what else is needed?

def translate_coroutine_types(lines: list[str]) -> list[str]:
coroutine_types = [line for line in lines if "Coroutine[" in line]
for type in coroutine_types:
res = re.search(r"Coroutine\[([A-z]+), ([A-z]+), ([A-z]+)\]", type)
if res:
old = res[0]
index = lines.index(type)
new = type.replace(old, res.group(3))
lines[index] = new
return lines

lines = translate_coroutine_types(lines)

If so:

diff --git a/tools/synchro.py b/tools/synchro.py
index e3d48355..640af42b 100644
--- a/tools/synchro.py
+++ b/tools/synchro.py
@@ -287,6 +287,7 @@ def process_files(
             with open(file, "r+") as f:
                 lines = f.readlines()
                 lines = apply_is_sync(lines, file)
+                lines = translate_awaitable_types(lines)
                 lines = translate_coroutine_types(lines)
                 lines = translate_async_sleeps(lines)
                 if file in docstring_translate_files:
@@ -313,6 +314,18 @@ def apply_is_sync(lines: list[str], file: str) -> list[str]:
     return lines
 
 
+def translate_awaitable_types(lines: list[str]) -> list[str]:
+    awaitable_types = [line for line in lines if "Awaitable[" in line]
+    for type in awaitable_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
+
+
 def translate_coroutine_types(lines: list[str]) -> list[str]:
     coroutine_types = [line for line in lines if "Coroutine[" in line]
     for type in coroutine_types:

@sleepyStick
Copy link
Contributor

sleepyStick commented Oct 17, 2025

Is the required change to tools/synchro.py simply adding and calling translate_awaitable_types, similar to the existing translate_coroutine_types?

That looks about right -- @aclark4life can you confirm?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.