Skip to content

Conversation

@Joshua-Ward1
Copy link

@Joshua-Ward1 Joshua-Ward1 commented Dec 10, 2025

Fixes gh-142418.

This PR updates inspect.iscoroutinefunction() so that coroutine markers
applied via inspect.markcoroutinefunction() are correctly detected on
wrapped callables, including functools.partial and
functools.partialmethod objects.

Previously, iscoroutinefunction() only checked the marker after unwrapping
the callable, which caused false negatives for marked partial and
partialmethod objects. The new logic checks for the marker at each unwrap
stage, with cycle protection, ensuring that any explicitly marked wrapper is
recognized as a coroutine function.

This change also adds regression tests verifying correct behavior for marked
and unmarked functools.partial and functools.partialmethod objects, and
includes a NEWS entry documenting the fix.


📚 Documentation preview 📚: https://cpython-previews--142503.org.readthedocs.build/

@bedevere-app
Copy link

bedevere-app bot commented Dec 10, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@bedevere-app
Copy link

bedevere-app bot commented Dec 10, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

Updated to follow format
@bedevere-app
Copy link

bedevere-app bot commented Dec 10, 2025

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

…ne_mark

Partial and method objects cannot form reference cycles in this context, so
the cycle-detection logic in _has_coroutine_mark introduced unnecessary
overhead. This change removes the visited-set check and keeps the function
as a simple unwrapping loop while still correctly detecting explicitly
marked coroutine wrappers.
Comment on lines +319 to +323
# Functions created by partialmethod descriptors keep a __partialmethod__ reference
pm = getattr(f, "__partialmethod__", None)
if isinstance(pm, functools.partialmethod):
f = pm
continue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this can also be moved forward by one block to avoid the time spent on obtaining the attribute when it is not necessary. I hope I have not bored you with these micro-optimizations.

continue

# partial and partialmethod share .func
if isinstance(f, (functools.partial, functools.partialmethod)):
Copy link

@x42005e1f x42005e1f Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will also add, "for the record", why I do not handle partialmethod objects in this way in the code attached to the original issue. They are not callable, and therefore applying markcoroutinefunction() to them is incorrect, which means they should not be checked. Being defined as a class member, accessing the corresponding attribute will return a regular function object created by partialmethod (or a method object for such a function, if via an instance). Therefore, there is no point in unnecessary iteration, and you can go straight to pm.func (see the block above).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inspect.iscoroutinefunction() does not detect marked partial objects

2 participants