diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2020-12-08 18:04:25 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@bbpush.zzzcomputing.com> | 2020-12-08 18:04:25 +0000 |
| commit | c5831b1abd98c46ef7eab7ee82ead18756aea112 (patch) | |
| tree | 4699cf11577807337e92edb197c8a52b4d5969af /lib/sqlalchemy | |
| parent | 18b1b261ff988549e75b011f2f4296fb13b24d64 (diff) | |
| parent | 76d90152302461637cfecb6c0cac65a50975c570 (diff) | |
| download | sqlalchemy-c5831b1abd98c46ef7eab7ee82ead18756aea112.tar.gz | |
Merge "Detect non compatible execution in async mode"
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/exc.py | 13 | ||||
| -rw-r--r-- | lib/sqlalchemy/ext/asyncio/engine.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/testing/asyncio.py | 14 | ||||
| -rw-r--r-- | lib/sqlalchemy/util/_concurrency_py3k.py | 12 |
4 files changed, 23 insertions, 19 deletions
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py index 7ba2e369b..63c56c34d 100644 --- a/lib/sqlalchemy/exc.py +++ b/lib/sqlalchemy/exc.py @@ -285,6 +285,15 @@ class NoReferenceError(InvalidRequestError): """Raised by ``ForeignKey`` to indicate a reference cannot be resolved.""" +class AwaitRequired(InvalidRequestError): + """Error raised by the async greenlet spawn if no async operation + was awaited when it required one + + """ + + code = "xd1r" + + class NoReferencedTableError(NoReferenceError): """Raised by ``ForeignKey`` when the referred ``Table`` cannot be located. @@ -355,10 +364,6 @@ class DontWrapMixin(object): """ -# Moved to orm.exc; compatibility definition installed by orm import until 0.6 -UnmappedColumnError = None - - class StatementError(SQLAlchemyError): """An error occurred during execution of a SQL statement. diff --git a/lib/sqlalchemy/ext/asyncio/engine.py b/lib/sqlalchemy/ext/asyncio/engine.py index 9e4851dfc..16edcc2b2 100644 --- a/lib/sqlalchemy/ext/asyncio/engine.py +++ b/lib/sqlalchemy/ext/asyncio/engine.py @@ -243,6 +243,7 @@ class AsyncConnection(StartableContext, AsyncConnectable): statement, parameters, execution_options, + _require_await=True, ) if result.context._is_server_side: raise async_exc.AsyncMethodRequired( @@ -272,6 +273,7 @@ class AsyncConnection(StartableContext, AsyncConnectable): util.EMPTY_DICT.merge_with( execution_options, {"stream_results": True} ), + _require_await=True, ) if not result.context._is_server_side: # TODO: real exception here @@ -322,6 +324,7 @@ class AsyncConnection(StartableContext, AsyncConnectable): statement, parameters, execution_options, + _require_await=True, ) if result.context._is_server_side: raise async_exc.AsyncMethodRequired( diff --git a/lib/sqlalchemy/testing/asyncio.py b/lib/sqlalchemy/testing/asyncio.py deleted file mode 100644 index 2e274de16..000000000 --- a/lib/sqlalchemy/testing/asyncio.py +++ /dev/null @@ -1,14 +0,0 @@ -from .assertions import assert_raises as _assert_raises -from .assertions import assert_raises_message as _assert_raises_message -from ..util import await_fallback as await_ -from ..util import greenlet_spawn - - -async def assert_raises_async(except_cls, msg, coroutine): - await greenlet_spawn(_assert_raises, except_cls, await_, coroutine) - - -async def assert_raises_message_async(except_cls, msg, coroutine): - await greenlet_spawn( - _assert_raises_message, except_cls, msg, await_, coroutine - ) diff --git a/lib/sqlalchemy/util/_concurrency_py3k.py b/lib/sqlalchemy/util/_concurrency_py3k.py index dcee05713..8ad3be543 100644 --- a/lib/sqlalchemy/util/_concurrency_py3k.py +++ b/lib/sqlalchemy/util/_concurrency_py3k.py @@ -79,7 +79,9 @@ def await_fallback(awaitable: Coroutine) -> Any: return current.driver.switch(awaitable) -async def greenlet_spawn(fn: Callable, *args, **kwargs) -> Any: +async def greenlet_spawn( + fn: Callable, *args, _require_await=False, **kwargs +) -> Any: """Runs a sync function ``fn`` in a new greenlet. The sync function can then use :func:`await_` to wait for async @@ -95,9 +97,11 @@ async def greenlet_spawn(fn: Callable, *args, **kwargs) -> Any: # is interrupted by await_, context is not dead and result is a # coroutine to wait. If the context is dead the function has # returned, and its result can be returned. + switch_occurred = False try: result = context.switch(*args, **kwargs) while not context.dead: + switch_occurred = True try: # wait for a coroutine from await_ and then return its # result back to it. @@ -112,6 +116,12 @@ async def greenlet_spawn(fn: Callable, *args, **kwargs) -> Any: finally: # clean up to avoid cycle resolution by gc del context.driver + if _require_await and not switch_occurred: + raise exc.AwaitRequired( + "The current operation required an async execution but none was " + "detected. This will usually happen when using a non compatible " + "DBAPI driver. Please ensure that an async DBAPI is used." + ) return result |
