Blob Blame History Raw
From 297031687679762849dedeaf24aa3a19116f095b Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com>
Date: Thu, 2 Dec 2021 11:26:20 +0100
Subject: [PATCH 1/2] Don't use asyncio.coroutinefunction if it's not available

Python 3.11 drops the deprecated @asyncio.coroutine and
asyncio.iscoroutinefunction.

Using a wrapper with @asyncio.coroutine in __get__ wasn't
necessary (the future from asyncio.ensure_future is awaitable,
and the wrapper doesn't do anything asynchronous), so the
logic can be simplified to just call asyncio.ensure_future
(to schedule the task and store the result when it's
available).

Tests for @asyncio.coroutine are skipped on 3.11+.

An unnecessary call to asyncio.coroutine in tests is
removed: it's not necessary to call this for `async def`
functions.
---
 cached_property.py                  |   24 +++++++++++-------------
 conftest.py                         |    8 ++++----
 tests/test_async_cached_property.py |    3 +--
 3 files changed, 16 insertions(+), 19 deletions(-)

--- a/cached_property.py
+++ b/cached_property.py
@@ -13,6 +13,12 @@ try:
     import asyncio
 except (ImportError, SyntaxError):
     asyncio = None
+if asyncio:
+    try:
+        iscoroutinefunction = asyncio.iscoroutinefunction
+    except AttributeError:
+        # Python 3.11: @asyncio.coroutine was removed
+        from inspect import iscoroutinefunction
 
 
 class cached_property(object):
@@ -30,22 +36,14 @@ class cached_property(object):
         if obj is None:
             return self
 
-        if asyncio and asyncio.iscoroutinefunction(self.func):
-            return self._wrap_in_coroutine(obj)
+        if asyncio and iscoroutinefunction(self.func):
+            value = asyncio.ensure_future(self.func(obj))
+        else:
+            value = self.func(obj)
 
-        value = obj.__dict__[self.func.__name__] = self.func(obj)
+        obj.__dict__[self.func.__name__] = value
         return value
 
-    def _wrap_in_coroutine(self, obj):
-        @wraps(obj)
-        @asyncio.coroutine
-        def wrapper():
-            future = asyncio.ensure_future(self.func(obj))
-            obj.__dict__[self.func.__name__] = future
-            return future
-
-        return wrapper()
-
 
 class threaded_cached_property(object):
     """
--- a/conftest.py
+++ b/conftest.py
@@ -1,4 +1,3 @@
-
 import sys
 
 # Whether "import asyncio" works
@@ -7,13 +6,14 @@ has_asyncio = sys.version_info[0] == 3 a
 # Whether the async and await keywords work
 has_async_await = sys.version_info[0] == 3 and sys.version_info[1] >= 5
 
+# Whether "from asyncio import coroutine" *fails*
+dropped_asyncio_coroutine = sys.version_info[0] == 3 and sys.version_info[1] >= 11
 
-print("conftest.py", has_asyncio, has_async_await)
-
+print("conftest.py", has_asyncio, has_async_await, dropped_asyncio_coroutine)
 
 collect_ignore = []
 
-if not has_asyncio:
+if not has_asyncio or dropped_asyncio_coroutine:
     collect_ignore.append("tests/test_coroutine_cached_property.py")
 
 if not has_async_await:
--- a/tests/test_async_cached_property.py
+++ b/tests/test_async_cached_property.py
@@ -9,8 +9,7 @@ import cached_property
 
 def unittest_run_loop(f):
     def wrapper(*args, **kwargs):
-        coro = asyncio.coroutine(f)
-        future = coro(*args, **kwargs)
+        future = f(*args, **kwargs)
         loop = asyncio.get_event_loop()
         loop.run_until_complete(future)