-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
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
bpo-34890: Make iscoroutinefunction, isgeneratorfunction and isasyncgenfunction work with functools.partial #9903
Conversation
…work with partial
Lib/inspect.py
Outdated
object.__code__.co_flags & CO_GENERATOR) | ||
is_partial_generator = bool((isinstance(object, functools.partial) and | ||
object.func.__code__.co_flags & CO_GENERATOR)) | ||
return is_generator_function or is_partial_generator |
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.
Wouldn't it be better to do this:
while isinstance(object, functools.partial):
object = object.func
? The benefit would be support of nested partials and the code will probably look less ugly.
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.
Also, while you're here, please rename object
to obj
or o
.
Lib/inspect.py
Outdated
"""Return true if the object is an asynchronous generator function. | ||
|
||
Asynchronous generator functions are defined with "async def" | ||
syntax and have "yield" expressions in their body. | ||
""" | ||
return bool((isfunction(object) or ismethod(object)) and | ||
object.__code__.co_flags & CO_ASYNC_GENERATOR) | ||
while isinstance(obj, functools.partial): |
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 don't we add an _unwrap_partial
helper. Or.. should we maybe use inspect.unwrap()
too?
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.
Actually, let's not use inspect.unwrap()
. Decorating generators and coroutines isn't that common.
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.
Do we still want the helper?
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 have done the refactor in 3b841ac
Lib/test/test_inspect.py
Outdated
@@ -2,6 +2,7 @@ | |||
import collections | |||
import datetime | |||
import functools | |||
from functools import partial |
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 use module import (i.e. import functools
).
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.
Done in adb5db6. I did that originally to simplify the double functools.partial
wrapping in the tests.
Hummmm, it seems that this changes the behaviour of def coroutine(func):
"""Decorator to mark coroutines.
If the coroutine is not yielded from before it is destroyed,
an error message is logged.
"""
if inspect.iscoroutinefunction(func):
# In Python 3.5 that's all we need to do for coroutines
# defined with "async def".
return func
if inspect.isgeneratorfunction(func):
coro = func
else:
@functools.wraps(func)
def coro(*args, **kw): this makes the function not being wrapped in @1st1 I don't know if this has a bit more impact than expected. Should I fix this by adding an extra check for |
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 remove new checks from asyncio.coroutine
; AFAICT we don't need them.
Lib/asyncio/coroutines.py
Outdated
@@ -107,12 +107,14 @@ def coroutine(func): | |||
If the coroutine is not yielded from before it is destroyed, | |||
an error message is logged. | |||
""" | |||
if inspect.iscoroutinefunction(func): | |||
if (inspect.iscoroutinefunction(func) | |||
and not isinstance(func, functools.partial)): |
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.
We don't need extra checks in this function. Here, we check for an async def
coroutine -- we don't need to wrap them in a CoroWrapper
ever.
Lib/asyncio/coroutines.py
Outdated
# In Python 3.5 that's all we need to do for coroutines | ||
# defined with "async def". | ||
return func | ||
|
||
if inspect.isgeneratorfunction(func): | ||
if (inspect.isgeneratorfunction(func) | ||
and not isinstance (func, functools.partial)): |
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.
We don't need this extra check either -- if func
is a generator function, it means that we don't need to wrap it into a proxy to force it to return a generator.
When you're done making the requested changes, leave the comment: |
@1st1 If I remove those checks then https://github.com/python/cpython/blob/master/Lib/test/test_asyncio/test_tasks.py#L425 will fail because the partials are not wrapped anymore and the This is an example failure of that test for clarity:
|
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 like the overall change.
@1st1: Would you mind to review this change?
Lib/inspect.py
Outdated
def _unwrap_partial(func): | ||
while isinstance(func, functools.partial): | ||
func = func.func | ||
return func |
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 suggest to move this helper function to Lib/functools.py.
@@ -0,0 +1,3 @@ | |||
Make :func:`inspect.iscoroutinefunction`, | |||
:func:`inspect.isgeneratorfunction` and :func:`inspect.isasyncgenfunction` | |||
work with :func:`functools.partial`. Patch by Pablo Galindo. |
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.
Maybe also document the change with a ".. versionchanged:: 3.8" markup in https://docs.python.org/dev/library/inspect.html#inspect.isgeneratorfunction ?
(Oh, @1st1 already started to review this change, good ;-)) |
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.
LGTM, but I would prefer to see a second review from a core (@1st1 maybe?).
It's not a problem. Now that |
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.
Sorry for the delayed review; I've been extremely busy last couple of weeks.
@1st1 Thank you very much for the review and the advice, Yuri! :) |
See also #6448 for a very related problem. |
# Helper functions | ||
|
||
def _unwrap_partial(func): | ||
while isinstance(func, partial): |
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.
@pablogsal BTW this doesn't handle the case of partialmethod
. Should we add that?
I have added some extra test cases for
isasyncgenfunction
andisasyncgen
intest_iscoroutine
for symmetry, but I am not sure if that is the place for it. Tell me if I should reallocate these tests in other place.https://bugs.python.org/issue34890