-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
gh-142418 Enhance markcoroutinefunction to support functools.partial #142505
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
Open
chaope
wants to merge
1
commit into
python:main
Choose a base branch
from
chaope:fix-issue-142418
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+14
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
Misc/NEWS.d/next/Library/2025-12-10-02-31-08.gh-issue-142418.XLGNh3.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Fix markcoroutinefunction by letting it set the coroutine function marker on | ||
| underlying function correctly. |
Oops, something went wrong.
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.
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 example in the original issue carefully. The original function may not always return a coroutine object depending on the parameters passed. Marking it will break the expected behavior because the return type of the partial function and the original function are different.
Uh oh!
There was an error while loading. Please reload this page.
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.
My understanding of the design of markcoroutinefunction and iscoroutinefunction is that it gives you a way to mark a function as coroutine, but it doesn't do any check to prevent you from falsely mark a random object as coroutine. We can have another issue discussing the design of it and if we do want to change the design of it, maybe we need a PEP.
But the issue you reported indeed reveals a bug that after a function is marked as coroutine, it is not correctly recognized by iscoroutinefunction (which is against what markcoroutinefunction and iscoroutinefunction are designed for).
To demonstrate that markcoroutinefunction can be applied to random object in python, you can try the following code, the object doesn't even need to be a callable.
Uh oh!
There was an error while loading. Please reload this page.
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.
How does this relate to my comment? If you unwrap an object in
markcoroutinefunction(), you do not mark the passed object (instead, you mark the one it references). And this violates both the expected and current behavior.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.
As
markcoroutinefunctionalready hasI don't think
marking the passed objectis the expected behavior.When we do
markcoroutinefunctionfor object A, the expected behavior is thatiscoroutinefunctionreturnsTruefor object A.current
iscoroutinefunctioncode does the unwrap, which means that all of the partial function sharing the same base function should return same result foriscoroutinefunction, I think this is the right behavior, and I want to preserve it, so that my change is done onmarkcoroutinefunction.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.
Current behavior:
Expected behavior:
Your behavior:
Uh oh!
There was an error while loading. Please reload this page.
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 note that the main difference between method objects and partial objects is that the former cannot change the return value (they expect the same parameters, except for
self), while the latter can narrow it down (since they can directly affect the parameters).(However, in special cases, method objects can also narrow the return type, but these are very exotic cases, which cannot be said about partial objects).
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.
Let me clarify this point. The reason why
iscoroutinefunction()returnsTruefor any wrapping partial object may be that the marked function already hasCoroutineTypeas its return type (you can see this even in the typeshed annotations). No matter how much we narrow this type, it will still remain a coroutine. And this rule must not be broken, otherwise we may get type errors that cannot be detected statically.Uh oh!
There was an error while loading. Please reload this page.
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 understand your concern and the expected behavior you provided. Using the variable name in your issue, ideally,
iscoroutinefunction(manufacturer_of_jokes)should not be True.But then it's a question of if we want to unwrap partial function in
iscoroutinefunction.iscoroutinefunction.jokecan again be wrapped in partial function, what should the wrapper function return then?Uh oh!
There was an error while loading. Please reload this page.
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.
A solution is to check the marker for each unwrapped object (including the passed object itself, if it is not a method object). You can see this in the code attached to the original issue and in the parallel PR. Once the marker is found, any subsequent narrowed type is guaranteed to be
CoroutineType(as long as the function is marked correctly).See the answer above. All explained via the type narrowing principle. Your
joke()has an arbitrary return type (Any) because it can accept an arbitrary function whose value it returns when passed (applyingmarkcoroutinefunction()to it is a big mistake).joke1()is a coroutine function and should be marked accordingly.joke2()is a string function and should not be considered a coroutine function. Your behavior violates the latter.Uh oh!
There was an error while loading. Please reload this page.
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
iscoroutinefunction()returnsTrue, this should be read as "the return type is alwaysCoroutineType(or one of its subclasses)" rather than "the return type may beCoroutineType(or one of its subclasses)". Imagine you are doing the following:This is error-prone if the return value is not always a coroutine (and in general, what is the point of different behavior, why would
iscoroutinefunction()be needed at all if it does not provide such a guarantee as long as functions are marked correctly?).