mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-18 06:32:08 -04:00
Improve error message when inspecting async proxies
Provide better error message when trying to insepct and async engine or asnyc connection. Change-Id: I907f3a22c6b76fe43df9d40cb0e69c57f74a7982
This commit is contained in:
committed by
Mike Bayer
parent
9131a5208f
commit
1798c3cf1c
Vendored
+28
@@ -1265,6 +1265,34 @@ attempt, which is unsupported when using SQLAlchemy with AsyncIO dialects.
|
||||
:ref:`asyncio_orm_avoid_lazyloads` - covers most ORM scenarios where
|
||||
this problem can occur and how to mitigate.
|
||||
|
||||
.. _error_xd3s:
|
||||
|
||||
No Inspection Avaliable
|
||||
-----------------------
|
||||
|
||||
Using the :func:`_sa.inspect` function directly on an
|
||||
:class:`_asyncio.AsyncConnection` or :class:`_asyncio.AsyncEngine` object is
|
||||
not currently supported, as there is not yet an awaitable form of the
|
||||
:class:`_reflection.Inspector` object available. Instead, the object
|
||||
is used by acquiring it using the
|
||||
:func:`_sa.inspect` function in such a way that it refers to the underlying
|
||||
:attr:`_asyncio.AsyncConnection.sync_connection` attribute of the
|
||||
:class:`_asyncio.AsyncConnection` object; the :class:`_engine.Inspector` is
|
||||
then used in a "synchronous" calling style by using the
|
||||
:meth:`_asyncio.AsyncConnection.run_sync` method along with a custom function
|
||||
that performs the desired operations::
|
||||
|
||||
async def async_main():
|
||||
async with engine.connect() as conn:
|
||||
tables = await conn.run_sync(
|
||||
lambda sync_conn: inspect(sync_conn).get_table_names()
|
||||
)
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`asyncio_inspector` - additional examples of using :func:`_sa.inspect`
|
||||
with the asyncio extension.
|
||||
|
||||
|
||||
Core Exception Classes
|
||||
======================
|
||||
|
||||
Vendored
+2
@@ -498,6 +498,8 @@ the usual ``await`` keywords are necessary, including for the
|
||||
.. currentmodule:: sqlalchemy.ext.asyncio
|
||||
|
||||
|
||||
.. _asyncio_inspector:
|
||||
|
||||
Using the Inspector to inspect schema objects
|
||||
---------------------------------------------------
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from . import exc as async_exc
|
||||
class ReversibleProxy:
|
||||
# weakref.ref(async proxy object) -> weakref.ref(sync proxied object)
|
||||
_proxy_objects = {}
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
def _assign_proxied(self, target):
|
||||
if target is not None:
|
||||
@@ -46,6 +47,8 @@ class ReversibleProxy:
|
||||
|
||||
|
||||
class StartableContext(abc.ABC):
|
||||
__slots__ = ()
|
||||
|
||||
@abc.abstractmethod
|
||||
async def start(self, is_ctxmanager=False):
|
||||
pass
|
||||
@@ -68,6 +71,8 @@ class StartableContext(abc.ABC):
|
||||
|
||||
|
||||
class ProxyComparable(ReversibleProxy):
|
||||
__slots__ = ()
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from .base import ProxyComparable
|
||||
from .base import StartableContext
|
||||
from .result import AsyncResult
|
||||
from ... import exc
|
||||
from ... import inspection
|
||||
from ... import util
|
||||
from ...engine import create_engine as _create_engine
|
||||
from ...engine.base import NestedTransaction
|
||||
@@ -80,6 +81,7 @@ class AsyncConnection(ProxyComparable, StartableContext, AsyncConnectable):
|
||||
# create a new AsyncConnection that matches this one given only the
|
||||
# "sync" elements.
|
||||
__slots__ = (
|
||||
"engine",
|
||||
"sync_engine",
|
||||
"sync_connection",
|
||||
)
|
||||
@@ -709,3 +711,24 @@ def _get_sync_engine_or_connection(async_engine):
|
||||
raise exc.ArgumentError(
|
||||
"AsyncEngine expected, got %r" % async_engine
|
||||
) from e
|
||||
|
||||
|
||||
@inspection._inspects(AsyncConnection)
|
||||
def _no_insp_for_async_conn_yet(subject):
|
||||
raise exc.NoInspectionAvailable(
|
||||
"Inspection on an AsyncConnection is currently not supported. "
|
||||
"Please use ``run_sync`` to pass a callable where it's possible "
|
||||
"to call ``inspect`` on the passed connection.",
|
||||
code="xd3s",
|
||||
)
|
||||
|
||||
|
||||
@inspection._inspects(AsyncEngine)
|
||||
def _no_insp_for_async_engine_xyet(subject):
|
||||
raise exc.NoInspectionAvailable(
|
||||
"Inspection on an AsyncEngine is currently not supported. "
|
||||
"Please obtain a connection then use ``conn.run_sync`` to pass a "
|
||||
"callable where it's possible to call ``inspect`` on the "
|
||||
"passed connection.",
|
||||
code="xd3s",
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlalchemy import delete
|
||||
from sqlalchemy import event
|
||||
from sqlalchemy import exc
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import String
|
||||
@@ -653,6 +654,39 @@ class AsyncEventTest(EngineFixture):
|
||||
[mock.call(sync_conn, mock.ANY, "select 1", (), mock.ANY, False)],
|
||||
)
|
||||
|
||||
@async_test
|
||||
async def test_event_on_sync_connection(self, async_engine):
|
||||
canary = mock.Mock()
|
||||
|
||||
async with async_engine.connect() as conn:
|
||||
event.listen(conn.sync_connection, "begin", canary)
|
||||
async with conn.begin():
|
||||
eq_(
|
||||
canary.mock_calls,
|
||||
[mock.call(conn.sync_connection)],
|
||||
)
|
||||
|
||||
|
||||
class AsyncInspection(EngineFixture):
|
||||
__backend__ = True
|
||||
|
||||
@async_test
|
||||
async def test_inspect_engine(self, async_engine):
|
||||
with testing.expect_raises_message(
|
||||
exc.NoInspectionAvailable,
|
||||
"Inspection on an AsyncEngine is currently not supported.",
|
||||
):
|
||||
inspect(async_engine)
|
||||
|
||||
@async_test
|
||||
async def test_inspect_connection(self, async_engine):
|
||||
async with async_engine.connect() as conn:
|
||||
with testing.expect_raises_message(
|
||||
exc.NoInspectionAvailable,
|
||||
"Inspection on an AsyncConnection is currently not supported.",
|
||||
):
|
||||
inspect(conn)
|
||||
|
||||
|
||||
class AsyncResultTest(EngineFixture):
|
||||
@testing.combinations(
|
||||
@@ -945,6 +979,7 @@ class AsyncProxyTest(EngineFixture, fixtures.TestBase):
|
||||
is_not(async_connection.engine, None)
|
||||
|
||||
@testing.requires.predictable_gc
|
||||
@async_test
|
||||
async def test_gc_engine(self, testing_engine):
|
||||
ReversibleProxy._proxy_objects.clear()
|
||||
|
||||
|
||||
@@ -580,12 +580,10 @@ class AsyncEventTest(AsyncFixture):
|
||||
|
||||
@async_test
|
||||
async def test_no_async_listeners(self, async_session):
|
||||
with testing.expect_raises(
|
||||
with testing.expect_raises_message(
|
||||
NotImplementedError,
|
||||
"NotImplementedError: asynchronous events are not implemented "
|
||||
"at this time. Apply synchronous listeners to the "
|
||||
"AsyncEngine.sync_engine or "
|
||||
"AsyncConnection.sync_connection attributes.",
|
||||
"asynchronous events are not implemented at this time. "
|
||||
"Apply synchronous listeners to the AsyncSession.sync_session.",
|
||||
):
|
||||
event.listen(async_session, "before_flush", mock.Mock())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user