Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpack partial in iscoroutinefunction #894

Merged
merged 2 commits into from
Jan 11, 2024
Merged

Conversation

maximlt
Copy link
Member

@maximlt maximlt commented Dec 20, 2023

We've observed that inspect.iscoroutinefunction doesn't return the same output of different versions of Python 3.10 when the function is a functools.partial. We make sure that param's iscoroutinefunction (that covers async generators too) unpacks partials before checking if they wrap a coroutine/async generator or not.

  • Before merging I would like to track down more precisely when things changed on CPython's side and if we see the same issue in Python 3.11 (if not, the unpacking could be removed when Python 3.10 support is dropped in some years), from memory we observed different results between CPython 3.10.5 and 3.10.13.

@maximlt
Copy link
Member Author

maximlt commented Dec 22, 2023

import inspect
from functools import partial
import sys

print(sys.version)

class Foo:
    async def foo(self, x=None):
        return

cb = partial(Foo().foo, x=1)
print(inspect.iscoroutinefunction(cb))

# 3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 07:05:37) [Clang 13.0.1 ]
# False

# 3.10.13 | packaged by conda-forge | (main, Oct 26 2023, 18:09:17) [Clang 16.0.6 ]
# True

I'm pretty sure this change occurred in python/cpython#94050. In our case f is not a method but a partial, that is unwrapped to a method, and a method is not a function so this used to return False. In the newer version with _signature_is_functionlike(f) that evaluates to True in our case the returned value is True. Anyway, what matters is that this has been backported to CPython 3.10 and 3.11, precisely to CPython 3.10.6 and 3.11.0 (backported before the actual release). So we could get rid of the partial unwrapping starting from Python 3.11.0.

def _has_code_flag(f, flag):
     while ismethod(f):
         f = f.__func__
     f = functools._unwrap_partial(f)
-    if not isfunction(f):
+    if not (isfunction(f) or _signature_is_functionlike(f)):
         return False
     return bool(f.__code__.co_flags & flag)

@philippjfr philippjfr merged commit 937b021 into main Jan 11, 2024
10 checks passed
@philippjfr philippjfr deleted the fix_iscoroutinefunction branch January 11, 2024 12:55
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.

None yet

2 participants