]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Improve error message when await_ call errors
authorFederico Caselli <cfederico87@gmail.com>
Tue, 12 Jan 2021 21:00:59 +0000 (22:00 +0100)
committerFederico Caselli <cfederico87@gmail.com>
Thu, 21 Jan 2021 20:38:05 +0000 (21:38 +0100)
Fixes: #5832
Change-Id: Ia2ed8f1d1ec54e5f6e1a8f817a69446fdb3b7f6d

doc/build/errors.rst
lib/sqlalchemy/exc.py
lib/sqlalchemy/util/_concurrency_py3k.py
test/base/test_concurrency_py3k.py

index 67a8a29b0f5aafb33ac84b5cab0ce67dac708ccd..7d767a7118ebef53a8aa02467ef691bcb23f681f 100644 (file)
@@ -1130,7 +1130,23 @@ with a non compatible :term:`DBAPI`.
 
 .. seealso::
 
-    :ref:`asyncio extension <asyncio_toplevel>`
+    :ref:`asyncio_toplevel`
+
+.. _error_xd2s:
+
+MissingGreenlet
+---------------
+
+A call to the async :term:`DBAPI` was initiated outside the greenlet spawn context
+usually setup by the SQLAlchemy AsyncIO proxy classes.
+Usually this error happens when an IO was attempted in an unexpected 
+place, without using the provided async api.
+When using the ORM this may be due to a lazy loading attempt, which
+is unsupported when using SQLAlchemy with AsyncIO dialects.
+
+.. seealso::
+
+    :ref:`_session_run_sync`
 
 
 Core Exception Classes
index 08b1bb060f2511e794005b07861d0d556388e452..289b8dfab1c4313b6a6a63c1f64f40b722961bd7 100644 (file)
@@ -294,6 +294,15 @@ class AwaitRequired(InvalidRequestError):
     code = "xd1r"
 
 
+class MissingGreenlet(InvalidRequestError):
+    r"""Error raised by the async greenlet await\_ if called while not inside
+    the greenlet spawn context.
+
+    """
+
+    code = "xd2s"
+
+
 class NoReferencedTableError(NoReferenceError):
     """Raised by ``ForeignKey`` when the referred ``Table`` cannot be
     located.
index 663d3e0f42cca07b028a02932d0a495a22b36c05..8edd057ef997b29d7a996fcd81824a7b668c629d 100644 (file)
@@ -44,8 +44,9 @@ def await_only(awaitable: Coroutine) -> Any:
     # this is called in the context greenlet while running fn
     current = greenlet.getcurrent()
     if not isinstance(current, _AsyncIoGreenlet):
-        raise exc.InvalidRequestError(
-            "greenlet_spawn has not been called; can't call await_() here."
+        raise exc.MissingGreenlet(
+            "greenlet_spawn has not been called; can't call await_() here. "
+            "Was IO attempted in an unexpected place?"
         )
 
     # returns the control to the driver greenlet passing it
@@ -69,9 +70,10 @@ def await_fallback(awaitable: Coroutine) -> Any:
     if not isinstance(current, _AsyncIoGreenlet):
         loop = asyncio.get_event_loop()
         if loop.is_running():
-            raise exc.InvalidRequestError(
+            raise exc.MissingGreenlet(
                 "greenlet_spawn has not been called and asyncio event "
-                "loop is already running; can't call await_() here."
+                "loop is already running; can't call await_() here. "
+                "Was IO attempted in an unexpected place?"
             )
         return loop.run_until_complete(awaitable)
 
index e7ae8c9ad203ffd8aadb4c0bfbde2a1b78301958..25ce9e7ecc6d9ab3a8db5a33a016d74afdcfed55 100644 (file)
@@ -57,7 +57,7 @@ class TestAsyncioCompat(fixtures.TestBase):
     async def test_await_only_no_greenlet(self):
         to_await = run1()
         with expect_raises_message(
-            exc.InvalidRequestError,
+            exc.MissingGreenlet,
             r"greenlet_spawn has not been called; can't call await_\(\) here.",
         ):
             await_only(to_await)
@@ -80,7 +80,7 @@ class TestAsyncioCompat(fixtures.TestBase):
             await_fallback(inner_await())
 
         with expect_raises_message(
-            exc.InvalidRequestError,
+            exc.MissingGreenlet,
             "greenlet_spawn has not been called and asyncio event loop",
         ):
             await greenlet_spawn(go)