You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
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
The reason will be displayed to describe this comment to others.Learn more.
For the check, we need to check plain functions and the__func__ cases,and the__call__ implementation for class instances. (Tests cover that: given that functions have a__call__ we need to be careful not to just check the one case.)
I looked at various ways of combining this into a single pass, but none that I found were particularly clear. Happy to take a suggestion, but I don't see this as being performance sensitive.
The implementation here is more sophisticated that the olderasyncio one…
I thought one maybe neater re-write looks like this:
diff --git a/Lib/inspect.py b/Lib/inspect.pyindex 883d0b02bf..3b9bd14a38 100644--- a/Lib/inspect.py+++ b/Lib/inspect.py@@ -410,12 +410,13 @@ def iscoroutinefunction(obj): Coroutine functions are normally defined with "async def" syntax, but may be marked via markcoroutinefunction. """- func = getattr(obj, "__func__", obj)- if getattr(func, "_is_coroutine", None) is _is_coroutine:- return True-- if not isclass(obj) and callable(obj) and getattr(obj.__call__, "_is_coroutine", None) is _is_coroutine:- return True+ if not isclass(obj) and callable(obj):+ # Test both the function and the __call__ implementation for the+ # _is_coroutine marker.+ f = getattr(getattr(obj, "__func__", obj), "_is_coroutine", None)+ c = getattr(obj.__call__, "_is_coroutine", None)+ if f is _is_coroutine or c is _is_coroutine:+ return True return _has_code_flag(obj, CO_COROUTINE) or ( not isclass(obj) and callable(obj) and _has_code_flag(obj.__call__, CO_COROUTINE)
The reason will be displayed to describe this comment to others.Learn more.
So perhaps a confusing aspect is thateverything that's callable has a__call__ method. Also the presence of__func__ indicates it's a class or instance method.
At least, those things seem to be obfuscating what's going on a bit.
Also, looking at the code for_has_code_flag(), I wonder if we don't need the same logic for checking the_is_coroutine flag.
What would happen if we wrote a helper function like this?
def _has_coroutine_mark(f): while ismethod(f): f = f.__func__ f = functools._unwrap_partial(f) if not (isfunction(f) or _signature_is_functionlike(f)): return False return getattr(f, "_is_coroutine", None) is _is_coroutine
(FWIW I wonder if the variable_is_coroutine shouldn't be renamed_is_coroutine_marker.)
Now, I'm not entirely sure why you're testing for__call__, when none of the otheris<something>function() predicates test for it. Maybe things would become simpler if we removed that feature?
We could then define our function like this:
def iscoroutinefunction(obj): return _has_code_flag(obj, CO_COROUTINE) or _has_coroutine_mark(obj)
Either way, I think it is desirable to returnTrue for class instance of classes such asCl with anasync def __call__()
So that's the reason for adding the additional... _has_code_flag(obj.__call__, ...) check.
In this PR, given that we were adding@staticmethod and@classmethod and so on, it seemed that we also should cover this kind of case:
class Cl2: @inspect.markcoroutinefunction def __call__(self): pass
Which leads to needing similar in the_is_coroutine_marker block.
The latter case is somewhat hypothetical... — it comes from trying to cover all the cases here, rather than from real-use™. To that extent I'd be happy to drop it, but I guess if we do there's an issue in X months time, saying "I need to do this". 🤔
I don't know the correct thing to say. (?)
(FWIW I wonder if the variable _is_coroutine shouldn't be renamed _is_coroutine_marker.)
Yep OK. +1
What would happen if we wrote a helper function like this?
Let me have a read.
Thanks so much for your thought and input on this.
The reason will be displayed to describe this comment to others.Learn more.
Hum, I still feel that you're sneaking in a new feature under the radar. :-) What this PR is supposed to fix isjust to add the ability to mark a sync function as "morally async". IOW the behavior of
class C: @inspect.markcoroutinefunction def __call__(self): ...print(inspect.iscoroutinefunction(C())
should be the same as
class C: async def __call__(self): ...print(inspect.iscoroutinefunction(C())
and since the latter printsFalse, I don't thinkthis PR has any business changing the output toTrue.
If you think the functionality used by Starlette deserves to be included in inspect, you should probably open a new issue for that first so it can be discussed properly. But I personally think this is overreaching (however, I am repeating myself).
Have you considered addingdef _has_coroutine_mark(f): as I suggested?
(Sorry to be such a pain. But once we let this in there's no way back, so I prefer to have the minimal necessary functionality only. Other core devs may differ though.)
not isclass(obj) and callable(obj) and _has_code_flag(obj.__call__, CO_COROUTINE)
)
Expand Down
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.