]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
raise ImportError when greenlet not installed
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Dec 2023 15:55:25 +0000 (10:55 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Dec 2023 18:21:52 +0000 (13:21 -0500)
This is the runtime raise when an async concurrency function
is called.  in 2.0 this raises ValueError, however here we've
standardized on raising ``ImportError`.

continuing for #10747, add a test asserting we dont get an
endless loop and get a clean ImportError instead when greenlet not
installed and async functions are used.

Fixes: #10747
Change-Id: I54dffe8577025e2ef3a59f5ca9ab7f4362d4d91f

lib/sqlalchemy/testing/requirements.py
lib/sqlalchemy/util/concurrency.py
test/base/test_concurrency.py

index f06ccd58bd1c10648224f56a6afeeb56c07d7cf6..eaba84ecd27eb99be4cb78df7d66653fe7eb1d15 100644 (file)
@@ -1603,6 +1603,18 @@ class SuiteRequirements(Requirements):
     def asyncio(self):
         return self.greenlet
 
+    @property
+    def no_greenlet(self):
+        def go(config):
+            try:
+                import greenlet  # noqa: F401
+            except ImportError:
+                return True
+            else:
+                return False
+
+        return exclusions.only_if(go)
+
     @property
     def greenlet(self):
         def go(config):
index 575d249c9ff43498284c80d42a3cef44af02899d..9e4c6c85da790ca65d13828552eab2d8a18476be 100644 (file)
@@ -15,6 +15,7 @@ from typing import Any
 from typing import Awaitable
 from typing import Callable
 from typing import Coroutine
+from typing import NoReturn
 from typing import Optional
 from typing import Protocol
 from typing import TYPE_CHECKING
@@ -62,6 +63,10 @@ if TYPE_CHECKING:
         ...
 
 
+def _not_implemented(*arg: Any, **kw: Any) -> NoReturn:
+    raise ImportError(_ERROR_MESSAGE)
+
+
 class _concurrency_shim_cls:
     """Late import shim for greenlet"""
 
@@ -78,7 +83,8 @@ class _concurrency_shim_cls:
             return
 
         if not TYPE_CHECKING:
-            global getcurrent, greenlet, _AsyncIoGreenlet, _has_gr_context
+            global getcurrent, greenlet, _AsyncIoGreenlet
+            global _has_gr_context, _greenlet_error
 
         try:
             from greenlet import getcurrent
@@ -120,6 +126,9 @@ class _concurrency_shim_cls:
 
     def _initialize_no_greenlet(self):
         self._util_async_run = self._no_greenlet_util_async_run
+        self.getcurrent = _not_implemented
+        self.greenlet = _not_implemented  # type: ignore
+        self._AsyncIoGreenlet = _not_implemented  # type: ignore
 
     def __getattr__(self, key: str) -> Any:
         if key in self.__slots__:
index 04d6e5208949eb8fc07099c63d0ddd54b6a04163..1ea61ba7cecf905551ac231f92ad136d2823ae1b 100644 (file)
@@ -290,3 +290,18 @@ class GreenletImportTests(fixtures.TestBase):
     )
     def test_concurrency_fn(self, fn):
         self._run_in_process(fn)
+
+
+class GracefulNoGreenletTest(fixtures.TestBase):
+    __requires__ = ("no_greenlet",)
+
+    def test_await_only_graceful(self):
+        async def async_fn():
+            pass
+
+        with expect_raises_message(
+            ImportError,
+            "The SQLAlchemy asyncio module requires that the Python "
+            "'greenlet' library is installed",
+        ):
+            await_only(async_fn())