-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-46622: Async support for cached_property #31314
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
Conversation
The functools.cached_property can now decorate an async function. It would cache a wrapper to the coroutine returned by the function, and await it when the wrapper is first awaited.
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 minor notes regarding the feature implementation.
Also, I'd like to get a consensus for the question on bpo first.
@@ -1000,6 +1026,8 @@ def __get__(self, instance, owner=None): | |||
val = cache.get(self.attrname, _NOT_FOUND) | |||
if val is _NOT_FOUND: | |||
val = self.func(instance) | |||
if inspect.iscoroutinefunction(self.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.
inspect.isawaitable(val)
check covers more cases, please use it.
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 would change the behaviour of
class MyAwaitable:
def __await__(self):
print("called")
class Foo:
@cached_property
def get_awaitable(self):
return MyAwaitable()
Currently this would cache the MyAwaitable
object and calls __await__
. If we use isawaitable
here, MyAwaitable
would be wrapped and only awaited once.
IMO iscoroutinefunction
is more correct here.
@@ -980,6 +1004,8 @@ def __set_name__(self, owner, name): | |||
) | |||
|
|||
def __get__(self, instance, owner=None): | |||
import inspect |
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 move the import just before usage, when the attribute name is not in cache yet.
The import instruction is not for free, it requires a lock at least IIRC.
The move allows returning cached results as fast as possible.
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 |
@@ -963,6 +963,30 @@ def __isabstractmethod__(self): | |||
_NOT_FOUND = object() | |||
|
|||
|
|||
class _CachedAwaitable: | |||
"""Wrapper to return if @cached_proeprty is used on a coroutine function. |
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.
spelling: cached_property
|
||
A coroutine shouldn't simply be cached since it can only be awaited once. | ||
This wrapper is cached instead, which awaits the underlying coroutine at | ||
most once and cache the result for subsequent calls. |
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.
"and caches"
Closing for now to work on |
The functools.cached_property can now decorate an async function. It would cache a wrapper to the coroutine returned by the function, and await it when the wrapper is first awaited.
https://bugs.python.org/issue46622