python / python/cpython

`inspect.iscoroutinefunction()` does not detect marked partial objects

Open
#142,418 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

inspect.markcoroutinefunction() is applied directly to the passed function, except for methods (which is correct behavior). However, inspect.iscoroutinefunction() checks the passed function only after unwrapping (a function that is not wrapped in either functools.partial or functools.partialmethod (currently, _has_coroutine_mark() does not handle these objects; why?)), and thus cannot detect the marker that is not at the end of the unwrapping chain, which results in false negative in cases where a functools.partial/functools.partialmethod object is marked.

>>> from functools import partial
>>> from inspect import iscoroutinefunction, markcoroutinefunction
>>> async def wedonotlikesnakecase():
...     return "the_funniest_joke_in_the_world"
>>> def manufacturer_of_jokes(somefunc):
...     global manufacturer_of_jokes
...     del manufacturer_of_jokes
...     return somefunc()
>>> joke = partial(manufacturer_of_jokes, wedonotlikesnakecase)
>>> joke = markcoroutinefunction(joke)
>>> iscoroutinefunction(joke)
False

I discovered this problem while theoretically considering backporting inspect.iscoroutinefunction() to older versions of Python. The problem has not yet been caused by any use case, so it is okay if the issue is closed due to lack of demand. But just in case, I am attaching part of how iscoroutinefunction() is implemented in my code.

def iscoroutinefunction(obj):
    marker_name, marker_value = _get_coroutinefunction_marker()

    while True:  # unwrap & check
        if ismethod(obj):
            obj = obj.__func__
            continue

        if marker_value is not MISSING:
            if getattr(obj, marker_name, MISSING) is marker_value:
                return True

        if isinstance(obj, partial):
            obj = obj.func
            continue

        impl = getattr(obj, _partialmethod_attribute_name, MISSING)

        if isinstance(impl, partialmethod):
            obj = impl.func
            continue

        # unlike its namesake in the `inspect` module, it does not unwrap
        return _has_code_flag(obj, CO_COROUTINE)
CPython versions tested on:

3.12, 3.13, 3.14

Operating systems tested on:

Linux

Linked PRs
  • gh-142503
  • gh-142505

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with inspect.iscoroutinefunction(), inspect.markcoroutinefunction(), and _has_coroutine_mark(), using the reproducer in the issue to confirm the false negative for a marked functools.partial. Review linked PRs gh-142503 and gh-142505 before starting; done means the reported partial-object case is detected correctly without regressing the existing method and unwrapping behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.