GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-142418: Fix inspect.iscoroutinefunction for marked functools.partial and partialmethod objects by Joshua-Ward1 · Pull Request #142503 · python/cpython

Conversation

@Joshua-Ward1

@Joshua-Ward1 Joshua-Ward1 commented Dec 10, 2025 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

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/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Joshua-Ward1 added 2 commits December 10, 2025 01:41
@Joshua-Ward1
Doc: clarify -i interaction with PYTHONSTARTUP
a51db30
@Joshua-Ward1
pythongh-142418: Fix inspect.iscoroutinefunction for marked functools…
1c4e6b9
….partial and partialmethod objects
@bedevere-app

bedevere-app Bot commented Dec 10, 2025

Copy link
Copy Markdown

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot added the awaiting review label Dec 10, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Dec 10, 2025
inspect.iscoroutinefunction() does not detect marked partial objects #142418
Open
@Joshua-Ward1
Fix NEWS entry formatting for pythongh-142418
245a701
Corrected the NEWS entry
@bedevere-app

bedevere-app Bot commented Dec 10, 2025

Copy link
Copy Markdown

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Joshua-Ward1
Rename NEWS entry to follow format
6084378
Updated to follow format
@bedevere-app

bedevere-app Bot commented Dec 10, 2025

Copy link
Copy Markdown

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Joshua-Ward1
Fixed Filename
6117107
x42005e1f
x42005e1f reviewed Dec 10, 2025
View reviewed changes
Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Joshua-Ward1 added 2 commits December 10, 2025 02:52
@Joshua-Ward1
pythongh-142418: Remove unnecessary cycle detection from _has_corouti…
dba7cb9
…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.
@Joshua-Ward1
pythongh-142418: Simplify _has_coroutine_mark by removing unnecessary…
9961179
… cycle-detection logic
@Joshua-Ward1
Joshua-Ward1 requested a review from x42005e1f December 10, 2025 08:10
x42005e1f
x42005e1f reviewed Dec 10, 2025
View reviewed changes
Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@Joshua-Ward1
pythongh-142418: Restore __partialmethod__ handling in _has_coroutine…
1f05728
…_mark
x42005e1f
x42005e1f reviewed Dec 10, 2025
View reviewed changes
Comment thread Lib/inspect.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@Joshua-Ward1
Reorder coroutine marker check after method unwrapping
5d5c324
x42005e1f
x42005e1f reviewed Dec 10, 2025
View reviewed changes
Comment thread Lib/inspect.py
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

@x42005e1f x42005e1f Dec 10, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@x42005e1f x42005e1f mentioned this pull request Dec 10, 2025
gh-142418 Enhance markcoroutinefunction to support functools.partial #142505
Closed
x42005e1f
x42005e1f reviewed Dec 10, 2025
View reviewed changes
Comment thread Lib/inspect.py
continue

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

@x42005e1f x42005e1f Dec 10, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

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).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
kumaraditya303
kumaraditya303 reviewed Dec 14, 2025
View reviewed changes
Comment thread Doc/using/cmdline.rst

Interactive mode will start even when :data:`sys.stdin` does not appear to be a terminal. The
:envvar:`PYTHONSTARTUP` file is not read.
In these "execute then interact" cases, Python runs the script or command

@kumaraditya303 kumaraditya303 Dec 14, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

This seems unrelated changes

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
picnixz
picnixz requested changes Dec 14, 2025
View reviewed changes
Comment thread Lib/inspect.py
f = f.__func__
f = functools._unwrap_partial(f)
return getattr(f, "_is_coroutine_marker", None) is _is_coroutine_mark
while True:

@picnixz picnixz Dec 14, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Please:

  • Wrap all lines under 80 characters.
  • Remove "obvious" comments. "Methods: unwrap first" is clear from the way you're doing it.
  • Avoid blank lines. The standard library usually tries to avoid expanding the code vertically.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/inspect.py
return getattr(f, "_is_coroutine_marker", None) is _is_coroutine_mark
while True:
# Methods: unwrap first (methods cannot be coroutine-marked)
if ismethod(f):

@picnixz picnixz Dec 14, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Why not doing while ismethod(f): f = f.__func__ here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@x42005e1f x42005e1f Dec 14, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

>>> from functools import partialmethod
>>> class MyClass:
...     def a(self): ...
...     b = partialmethod(a)
>>> obj = MyClass()
>>> obj.b
functools.partial(<bound method MyClass.a of <__main__.MyClass object at 0x7fd343f4cc20>>)
>>> obj.b.func.__func__
<function MyClass.a at 0x7fd343f42cf0>

When unwrapping the reference to obj.b, the functools.partial object comes before the method object, so the assumption that method objects always precede other objects is incorrect.

>>> from functools import partialmethod
>>> class MyFirstClass:
...     def f(self, other): ...
>>> first = MyFirstClass()
>>> class MySecondClass:
...     g = partialmethod(first.f)
>>> second = MySecondClass()
>>> second.g
<bound method partialmethod._make_unbound_method.<locals>._method of <__main__.MySecondClass object at 0x7fd343f4d400>>
>>> second.g.__func__.__partialmethod__.func
<bound method MyFirstClass.f of <__main__.MyFirstClass object at 0x7fd343f4d2b0>>

Well, it seems to me that the current implementation (in the main branch) is not very well thought out. Personally, I would not rely on it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@x42005e1f x42005e1f Dec 14, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

If your comment is only about why there is if instead of while... why do we need a nested loop when we already have an outer one? Moreover, how often are methods that refer to other methods used? Does it even make sense to try to optimize this case? I think it is sufficient and simpler to rely on the outer loop.

As you can easily see, the code in this PR is very similar to the one I attached to the original issue, except for the order of the blocks and some points borrowed from the original code (AI-generated or just copy-paste?). I use something similar in the implementation of similar functions in my library, although they are more general in nature.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/inspect.py
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

@picnixz picnixz Dec 14, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I think you should use functools._unwrap_partialmethod which handles both partial methods and partial functions (first it unwraps partial methods then unwraps partial functions)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@x42005e1f x42005e1f Dec 14, 2025

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Please read the discussion at #142505.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting changes and removed awaiting review labels Dec 14, 2025
@bedevere-app

bedevere-app Bot commented Dec 14, 2025

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown

This PR is stale because it has been open for 30 days with no activity.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@kumaraditya303 kumaraditya303 kumaraditya303 left review comments
@picnixz picnixz picnixz requested changes
+1 more reviewer
@x42005e1f x42005e1f x42005e1f left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting changes stale Stale PR or inactive for long period of time.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

inspect.iscoroutinefunction() does not detect marked partial objects

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@Joshua-Ward1 @picnixz @kumaraditya303 @x42005e1f
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.