summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2023-04-30 18:27:24 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2023-05-09 20:23:48 -0400
commit60b31198311eedfa3814e7098c94d3aa29338fdd (patch)
tree5fe3a55ef67ab14fa63a0a3122a1326b830ceb72
parent228490ead7048f2e558c25b0f055bdb952272ec4 (diff)
downloadsqlalchemy-60b31198311eedfa3814e7098c94d3aa29338fdd.tar.gz
fix test suite warnings
fix a handful of warnings that were emitting but not raising, usually because they were inside an "expect_warnings" block. modify "expect_warnings" to always use "raise_on_any_unexpected" behavior; remove this parameter. Fixed issue in semi-private ``await_only()`` and ``await_fallback()`` concurrency functions where the given awaitable would remain un-awaited if the function threw a ``GreenletError``, which could cause "was not awaited" warnings later on if the program continued. In this case, the given awaitable is now cancelled before the exception is thrown. Change-Id: I33668c5e8c670454a3d879e559096fb873b57244
-rw-r--r--doc/build/changelog/unreleased_20/await_cancel.rst8
-rw-r--r--doc/build/orm/queryguide/columns.rst2
-rw-r--r--examples/versioned_history/test_versioning.py2
-rw-r--r--lib/sqlalchemy/dialects/postgresql/asyncpg.py1
-rw-r--r--lib/sqlalchemy/dialects/sqlite/aiosqlite.py10
-rw-r--r--lib/sqlalchemy/orm/_orm_constructors.py1
-rw-r--r--lib/sqlalchemy/orm/query.py12
-rw-r--r--lib/sqlalchemy/testing/assertions.py42
-rw-r--r--lib/sqlalchemy/util/_concurrency_py3k.py27
-rw-r--r--test/aaa_profiling/test_memusage.py11
-rw-r--r--test/base/test_concurrency_py3k.py13
-rw-r--r--test/dialect/mysql/test_query.py33
-rw-r--r--test/engine/test_execute.py14
-rw-r--r--test/engine/test_pool.py5
-rw-r--r--test/ext/asyncio/test_session_py3k.py1
-rw-r--r--test/ext/test_deprecations.py4
-rw-r--r--test/orm/declarative/test_basic.py1
-rw-r--r--test/orm/declarative/test_inheritance.py1
-rw-r--r--test/orm/inheritance/test_relationship.py1
-rw-r--r--test/orm/test_collection.py3
-rw-r--r--test/orm/test_deprecations.py15
-rw-r--r--test/orm/test_eager_relations.py4
-rw-r--r--test/orm/test_joins.py1
-rw-r--r--test/orm/test_relationships.py89
-rw-r--r--test/orm/test_session.py26
-rw-r--r--test/orm/test_utils.py4
-rw-r--r--test/sql/test_delete.py8
-rw-r--r--test/sql/test_insert_exec.py3
28 files changed, 203 insertions, 139 deletions
diff --git a/doc/build/changelog/unreleased_20/await_cancel.rst b/doc/build/changelog/unreleased_20/await_cancel.rst
new file mode 100644
index 000000000..5d50a4fcc
--- /dev/null
+++ b/doc/build/changelog/unreleased_20/await_cancel.rst
@@ -0,0 +1,8 @@
+.. change::
+ :tags: bug, asyncio
+
+ Fixed issue in semi-private ``await_only()`` and ``await_fallback()``
+ concurrency functions where the given awaitable would remain un-awaited if
+ the function threw a ``GreenletError``, which could cause "was not awaited"
+ warnings later on if the program continued. In this case, the given
+ awaitable is now cancelled before the exception is thrown.
diff --git a/doc/build/orm/queryguide/columns.rst b/doc/build/orm/queryguide/columns.rst
index 255c1f902..93d0919ba 100644
--- a/doc/build/orm/queryguide/columns.rst
+++ b/doc/build/orm/queryguide/columns.rst
@@ -869,7 +869,7 @@ onto newly loaded instances of ``A``::
>>> orm_stmt = (
... select(User)
... .from_statement(union_stmt)
- ... .options(with_expression(User.book_count, union_stmt.c.book_count))
+ ... .options(with_expression(User.book_count, union_stmt.selected_columns.book_count))
... )
>>> for user in session.scalars(orm_stmt):
... print(f"Username: {user.name} Number of books: {user.book_count}")
diff --git a/examples/versioned_history/test_versioning.py b/examples/versioned_history/test_versioning.py
index 9caadc043..392a415ff 100644
--- a/examples/versioned_history/test_versioning.py
+++ b/examples/versioned_history/test_versioning.py
@@ -13,9 +13,9 @@ from sqlalchemy import Integer
from sqlalchemy import join
from sqlalchemy import select
from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import clear_mappers
from sqlalchemy.orm import column_property
+from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import deferred
from sqlalchemy.orm import exc as orm_exc
diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py
index c879205e4..d198620d3 100644
--- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py
+++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py
@@ -850,6 +850,7 @@ class AsyncAdapt_asyncpg_connection(AdaptedConnection):
def terminate(self):
self._connection.terminate()
+ self._started = False
@staticmethod
def _default_name_func():
diff --git a/lib/sqlalchemy/dialects/sqlite/aiosqlite.py b/lib/sqlalchemy/dialects/sqlite/aiosqlite.py
index f9c60efc1..2981976ac 100644
--- a/lib/sqlalchemy/dialects/sqlite/aiosqlite.py
+++ b/lib/sqlalchemy/dialects/sqlite/aiosqlite.py
@@ -239,6 +239,16 @@ class AsyncAdapt_aiosqlite_connection(AdaptedConnection):
def close(self):
try:
self.await_(self._connection.close())
+ except ValueError:
+ # this is undocumented for aiosqlite, that ValueError
+ # was raised if .close() was called more than once, which is
+ # both not customary for DBAPI and is also not a DBAPI.Error
+ # exception. This is now fixed in aiosqlite via my PR
+ # https://github.com/omnilib/aiosqlite/pull/238, so we can be
+ # assured this will not become some other kind of exception,
+ # since it doesn't raise anymore.
+
+ pass
except Exception as error:
self._handle_exception(error)
diff --git a/lib/sqlalchemy/orm/_orm_constructors.py b/lib/sqlalchemy/orm/_orm_constructors.py
index 563fef3c5..fab93f682 100644
--- a/lib/sqlalchemy/orm/_orm_constructors.py
+++ b/lib/sqlalchemy/orm/_orm_constructors.py
@@ -83,6 +83,7 @@ _T = typing.TypeVar("_T")
"The :class:`.AliasOption` object is not necessary "
"for entities to be matched up to a query that is established "
"via :meth:`.Query.from_statement` and now does nothing.",
+ enable_warnings=False, # AliasOption itself warns
)
def contains_alias(alias: Union[Alias, Subquery]) -> AliasOption:
r"""Return a :class:`.MapperOption` that will indicate to the
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 5cd7cc117..e6381bee1 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1436,7 +1436,13 @@ class Query(
to the given list of columns
"""
+ return self._values_no_warn(*columns)
+ _values = values
+
+ def _values_no_warn(
+ self, *columns: _ColumnsClauseArgument[Any]
+ ) -> Iterable[Any]:
if not columns:
return iter(())
q = self._clone().enable_eagerloads(False)
@@ -1445,8 +1451,6 @@ class Query(
q.load_options += {"_yield_per": 10}
return iter(q) # type: ignore
- _values = values
-
@util.deprecated(
"1.4",
":meth:`_query.Query.value` "
@@ -1460,7 +1464,7 @@ class Query(
"""
try:
- return next(self.values(column))[0] # type: ignore
+ return next(self._values_no_warn(column))[0] # type: ignore
except StopIteration:
return None
@@ -3332,7 +3336,7 @@ class AliasOption(interfaces.LoaderOption):
@util.deprecated(
"1.4",
- "The :class:`.AliasOption` is not necessary "
+ "The :class:`.AliasOption` object is not necessary "
"for entities to be matched up to a query that is established "
"via :meth:`.Query.from_statement` and now does nothing.",
)
diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py
index a51d831a9..e7b416167 100644
--- a/lib/sqlalchemy/testing/assertions.py
+++ b/lib/sqlalchemy/testing/assertions.py
@@ -47,7 +47,7 @@ def expect_warnings(*messages, **kw):
Note that the test suite sets SAWarning warnings to raise exceptions.
""" # noqa
- return _expect_warnings(sa_exc.SAWarning, messages, **kw)
+ return _expect_warnings_sqla_only(sa_exc.SAWarning, messages, **kw)
@contextlib.contextmanager
@@ -84,11 +84,15 @@ def emits_warning(*messages):
def expect_deprecated(*messages, **kw):
- return _expect_warnings(sa_exc.SADeprecationWarning, messages, **kw)
+ return _expect_warnings_sqla_only(
+ sa_exc.SADeprecationWarning, messages, **kw
+ )
def expect_deprecated_20(*messages, **kw):
- return _expect_warnings(sa_exc.Base20DeprecationWarning, messages, **kw)
+ return _expect_warnings_sqla_only(
+ sa_exc.Base20DeprecationWarning, messages, **kw
+ )
def emits_warning_on(db, *messages):
@@ -140,6 +144,29 @@ _SEEN = None
_EXC_CLS = None
+def _expect_warnings_sqla_only(
+ exc_cls,
+ messages,
+ regex=True,
+ search_msg=False,
+ assert_=True,
+):
+ """SQLAlchemy internal use only _expect_warnings().
+
+ Alembic is using _expect_warnings() directly, and should be updated
+ to use this new interface.
+
+ """
+ return _expect_warnings(
+ exc_cls,
+ messages,
+ regex=regex,
+ search_msg=search_msg,
+ assert_=assert_,
+ raise_on_any_unexpected=True,
+ )
+
+
@contextlib.contextmanager
def _expect_warnings(
exc_cls,
@@ -150,7 +177,6 @@ def _expect_warnings(
raise_on_any_unexpected=False,
squelch_other_warnings=False,
):
-
global _FILTERS, _SEEN, _EXC_CLS
if regex or search_msg:
@@ -181,7 +207,6 @@ def _expect_warnings(
real_warn = warnings.warn
def our_warn(msg, *arg, **kw):
-
if isinstance(msg, _EXC_CLS):
exception = type(msg)
msg = str(msg)
@@ -379,7 +404,7 @@ def assert_warns(except_cls, callable_, *args, **kwargs):
"""
- with _expect_warnings(except_cls, [".*"], squelch_other_warnings=True):
+ with _expect_warnings_sqla_only(except_cls, [".*"]):
return callable_(*args, **kwargs)
@@ -394,12 +419,11 @@ def assert_warns_message(except_cls, msg, callable_, *args, **kwargs):
rather than regex.match().
"""
- with _expect_warnings(
+ with _expect_warnings_sqla_only(
except_cls,
[msg],
search_msg=True,
regex=False,
- squelch_other_warnings=True,
):
return callable_(*args, **kwargs)
@@ -413,7 +437,6 @@ def assert_raises_message_context_ok(
def _assert_raises(
except_cls, callable_, args, kwargs, msg=None, check_context=False
):
-
with _expect_raises(except_cls, msg, check_context) as ec:
callable_(*args, **kwargs)
return ec.error
@@ -892,7 +915,6 @@ class AssertsExecutionResults:
return result
def assert_sql(self, db, callable_, rules):
-
newrules = []
for rule in rules:
if isinstance(rule, dict):
diff --git a/lib/sqlalchemy/util/_concurrency_py3k.py b/lib/sqlalchemy/util/_concurrency_py3k.py
index b29cc2119..0e26425b2 100644
--- a/lib/sqlalchemy/util/_concurrency_py3k.py
+++ b/lib/sqlalchemy/util/_concurrency_py3k.py
@@ -17,11 +17,13 @@ from typing import Awaitable
from typing import Callable
from typing import Coroutine
from typing import Optional
+from typing import TYPE_CHECKING
from typing import TypeVar
from .langhelpers import memoized_property
from .. import exc
from ..util.typing import Protocol
+from ..util.typing import TypeGuard
_T = TypeVar("_T")
@@ -78,6 +80,26 @@ class _AsyncIoGreenlet(greenlet): # type: ignore
self.gr_context = driver.gr_context
+_T_co = TypeVar("_T_co", covariant=True)
+
+if TYPE_CHECKING:
+
+ def iscoroutine(
+ awaitable: Awaitable[_T_co],
+ ) -> TypeGuard[Coroutine[Any, Any, _T_co]]:
+ ...
+
+else:
+ iscoroutine = asyncio.iscoroutine
+
+
+def _safe_cancel_awaitable(awaitable: Awaitable[Any]) -> None:
+ # https://docs.python.org/3/reference/datamodel.html#coroutine.close
+
+ if iscoroutine(awaitable):
+ awaitable.close()
+
+
def await_only(awaitable: Awaitable[_T]) -> _T:
"""Awaits an async function in a sync method.
@@ -90,6 +112,8 @@ def await_only(awaitable: Awaitable[_T]) -> _T:
# this is called in the context greenlet while running fn
current = getcurrent()
if not isinstance(current, _AsyncIoGreenlet):
+ _safe_cancel_awaitable(awaitable)
+
raise exc.MissingGreenlet(
"greenlet_spawn has not been called; can't call await_only() "
"here. Was IO attempted in an unexpected place?"
@@ -117,6 +141,9 @@ def await_fallback(awaitable: Awaitable[_T]) -> _T:
if not isinstance(current, _AsyncIoGreenlet):
loop = get_event_loop()
if loop.is_running():
+
+ _safe_cancel_awaitable(awaitable)
+
raise exc.MissingGreenlet(
"greenlet_spawn has not been called and asyncio event "
"loop is already running; can't call await_fallback() here. "
diff --git a/test/aaa_profiling/test_memusage.py b/test/aaa_profiling/test_memusage.py
index 9b2cb31be..dc5a39910 100644
--- a/test/aaa_profiling/test_memusage.py
+++ b/test/aaa_profiling/test_memusage.py
@@ -296,7 +296,6 @@ class MemUsageTest(EnsureZeroed):
@testing.requires.cextensions
def test_cycles_in_row(self):
-
tup = result.result_tuple(["a", "b", "c"])
@profile_memory()
@@ -695,7 +694,6 @@ class MemUsageWBackendTest(fixtures.MappedTest, EnsureZeroed):
@testing.emits_warning()
@profile_memory()
def go():
-
# execute with a non-unicode object. a warning is emitted,
# this warning shouldn't clog up memory.
@@ -1066,7 +1064,9 @@ class MemUsageWBackendTest(fixtures.MappedTest, EnsureZeroed):
t1_mapper = self.mapper_registry.map_imperatively(T1, t1)
- @testing.emits_warning()
+ @testing.emits_warning(
+ r"This declarative base", r"Property .* being replaced"
+ )
@profile_memory()
def go():
class T2:
@@ -1128,7 +1128,9 @@ class MemUsageWBackendTest(fixtures.MappedTest, EnsureZeroed):
s = table2.select()
sess = session()
with testing.expect_deprecated(
- "Implicit coercion of SELECT and " "textual SELECT constructs"
+ "Implicit coercion of SELECT and textual SELECT constructs",
+ "An alias is being generated automatically",
+ assert_=False,
):
sess.query(Foo).join(s, Foo.bars).all()
sess.rollback()
@@ -1637,7 +1639,6 @@ class CycleTest(_fixtures.FixtureTest):
@testing.provide_metadata
def test_optimized_get(self):
-
Base = declarative_base(metadata=self.metadata)
class Employee(Base):
diff --git a/test/base/test_concurrency_py3k.py b/test/base/test_concurrency_py3k.py
index 6a3098a6a..17a0fafb5 100644
--- a/test/base/test_concurrency_py3k.py
+++ b/test/base/test_concurrency_py3k.py
@@ -95,7 +95,12 @@ class TestAsyncioCompat(fixtures.TestBase):
):
await_only(to_await)
- # ensure no warning
+ # existing awaitable is done
+ with expect_raises(RuntimeError):
+ await greenlet_spawn(await_fallback, to_await)
+
+ # no warning for a new one...
+ to_await = run1()
await greenlet_spawn(await_fallback, to_await)
@async_test
@@ -118,7 +123,8 @@ class TestAsyncioCompat(fixtures.TestBase):
):
await greenlet_spawn(go)
- await to_await
+ with expect_raises(RuntimeError):
+ await to_await
@async_test
async def test_await_only_error(self):
@@ -141,7 +147,8 @@ class TestAsyncioCompat(fixtures.TestBase):
):
await greenlet_spawn(go)
- await to_await
+ with expect_raises(RuntimeError):
+ await to_await
@async_test
async def test_contextvars(self):
diff --git a/test/dialect/mysql/test_query.py b/test/dialect/mysql/test_query.py
index 0ce361182..921b5c52b 100644
--- a/test/dialect/mysql/test_query.py
+++ b/test/dialect/mysql/test_query.py
@@ -11,9 +11,9 @@ from sqlalchemy import or_
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy import Table
-from sqlalchemy import testing
from sqlalchemy import true
from sqlalchemy.testing import eq_
+from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
@@ -22,23 +22,26 @@ class IdiosyncrasyTest(fixtures.TestBase):
__only_on__ = "mysql", "mariadb"
__backend__ = True
- @testing.emits_warning()
def test_is_boolean_symbols_despite_no_native(self, connection):
+ with expect_warnings("Datatype BOOL does not support CAST"):
+ is_(
+ connection.scalar(select(cast(true().is_(true()), Boolean))),
+ True,
+ )
- is_(
- connection.scalar(select(cast(true().is_(true()), Boolean))),
- True,
- )
-
- is_(
- connection.scalar(select(cast(true().is_not(true()), Boolean))),
- False,
- )
+ with expect_warnings("Datatype BOOL does not support CAST"):
+ is_(
+ connection.scalar(
+ select(cast(true().is_not(true()), Boolean))
+ ),
+ False,
+ )
- is_(
- connection.scalar(select(cast(false().is_(false()), Boolean))),
- True,
- )
+ with expect_warnings("Datatype BOOL does not support CAST"):
+ is_(
+ connection.scalar(select(cast(false().is_(false()), Boolean))),
+ True,
+ )
class MatchTest(fixtures.TablesTest):
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py
index e99448a26..2c5f091b4 100644
--- a/test/engine/test_execute.py
+++ b/test/engine/test_execute.py
@@ -2044,6 +2044,7 @@ class EngineEventsTest(fixtures.TestBase):
select(1).compile(dialect=e1.dialect), (), {}
)
+ @testing.emits_warning("The garbage collector is trying to clean up")
def test_execute_events(self):
stmts = []
@@ -2283,6 +2284,7 @@ class EngineEventsTest(fixtures.TestBase):
eng_copy = copy.copy(eng)
eng_copy.dispose(close=close)
+
copy_conn = eng_copy.connect()
dbapi_conn_two = copy_conn.connection.dbapi_connection
@@ -2294,6 +2296,9 @@ class EngineEventsTest(fixtures.TestBase):
else:
is_(dbapi_conn_one, conn.connection.dbapi_connection)
+ conn.close()
+ copy_conn.close()
+
def test_retval_flag(self):
canary = []
@@ -3702,8 +3707,8 @@ class DialectEventTest(fixtures.TestBase):
conn.connection.invalidate(soft=True)
conn.close()
- conn = e.connect()
- eq_(conn.info["boom"], "one")
+ with e.connect() as conn:
+ eq_(conn.info["boom"], "one")
def test_connect_do_connect_info_there_after_invalidate(self):
# test that info is maintained after the do_connect()
@@ -3720,8 +3725,9 @@ class DialectEventTest(fixtures.TestBase):
eq_(conn.info["boom"], "one")
conn.connection.invalidate()
- conn = e.connect()
- eq_(conn.info["boom"], "one")
+
+ with e.connect() as conn:
+ eq_(conn.info["boom"], "one")
class SetInputSizesTest(fixtures.TablesTest):
diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py
index 6730d7012..cca6e2589 100644
--- a/test/engine/test_pool.py
+++ b/test/engine/test_pool.py
@@ -1705,6 +1705,7 @@ class QueuePoolTest(PoolTestBase):
)
@testing.combinations((True,), (False,))
+ @testing.emits_warning("The garbage collector")
def test_userspace_disconnectionerror_weakref_finalizer(self, detach_gced):
dbapi, pool = self._queuepool_dbapi_fixture(
pool_size=1, max_overflow=2, _is_asyncio=detach_gced
@@ -1737,6 +1738,7 @@ class QueuePoolTest(PoolTestBase):
not_closed_dbapi_conn = conn.dbapi_connection
del conn
+
gc_collect()
if detach_gced:
@@ -1744,6 +1746,9 @@ class QueuePoolTest(PoolTestBase):
eq_(not_closed_dbapi_conn.mock_calls, [])
else:
# new connection reset and returned to pool
+ # this creates a gc-level warning that is not easy to pin down,
+ # hence we use the testing.emits_warning() decorator just to squash
+ # it
eq_(not_closed_dbapi_conn.mock_calls, [call.rollback()])
@testing.requires.timing_intensive
diff --git a/test/ext/asyncio/test_session_py3k.py b/test/ext/asyncio/test_session_py3k.py
index 36135a43d..fed59995f 100644
--- a/test/ext/asyncio/test_session_py3k.py
+++ b/test/ext/asyncio/test_session_py3k.py
@@ -741,6 +741,7 @@ class AsyncORMBehaviorsTest(AsyncFixture):
(exc.StatementError, exc.MissingGreenlet)
):
a1.b = b2
+
else:
a1.b = b2
diff --git a/test/ext/test_deprecations.py b/test/ext/test_deprecations.py
index 97c4172ba..653a02157 100644
--- a/test/ext/test_deprecations.py
+++ b/test/ext/test_deprecations.py
@@ -70,11 +70,11 @@ class HorizontalShardTest(fixtures.TestBase):
m1 = mock.Mock()
with testing.expect_deprecated(
- "The ``query_chooser`` parameter is deprecated; please use"
+ "The ``query_chooser`` parameter is deprecated; please use",
):
s = ShardedSession(
shard_chooser=m1.shard_chooser,
- id_chooser=m1.id_chooser,
+ identity_chooser=m1.identity_chooser,
query_chooser=m1.query_chooser,
)
diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py
index d0e56819c..6dcecc0c1 100644
--- a/test/orm/declarative/test_basic.py
+++ b/test/orm/declarative/test_basic.py
@@ -1394,7 +1394,6 @@ class DeclarativeMultiBaseTest(
r"non-schema SQLAlchemy expression object; ",
r"Attribute 'y' on class <class .*Foo5.* appears to be a "
r"non-schema SQLAlchemy expression object; ",
- raise_on_any_unexpected=True,
):
class Foo5(Base):
diff --git a/test/orm/declarative/test_inheritance.py b/test/orm/declarative/test_inheritance.py
index 333d24230..7d66c0d0b 100644
--- a/test/orm/declarative/test_inheritance.py
+++ b/test/orm/declarative/test_inheritance.py
@@ -1026,7 +1026,6 @@ class DeclarativeInheritanceTest(
if not combine_on_b and not omit_from_statements:
ctx = expect_warnings(
"Implicitly combining column a.extra with column b.extra",
- raise_on_any_unexpected=True,
)
else:
ctx = contextlib.nullcontext()
diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py
index 6d168d2ce..ab9c29918 100644
--- a/test/orm/inheritance/test_relationship.py
+++ b/test/orm/inheritance/test_relationship.py
@@ -61,7 +61,6 @@ def _aliased_join_warning(arg):
return testing.expect_warnings(
r"An alias is being generated automatically against joined entity "
r"Mapper\[%s\] due to overlapping tables" % (arg,),
- raise_on_any_unexpected=True,
)
diff --git a/test/orm/test_collection.py b/test/orm/test_collection.py
index 650b30aaa..85efbe0a3 100644
--- a/test/orm/test_collection.py
+++ b/test/orm/test_collection.py
@@ -2928,7 +2928,6 @@ class AttrKeyedDictKeysTest(fixtures.TestBase):
"'bs' was None;",
"Attribute keyed dictionary value for attribute "
"'B.a' was None;",
- raise_on_any_unexpected=True,
):
a1 = A(bs={"k1": b1, "k2": b2})
sess.add(a1)
@@ -2943,7 +2942,6 @@ class AttrKeyedDictKeysTest(fixtures.TestBase):
with expect_warnings(
"Attribute keyed dictionary value for attribute "
"'unknown relationship' was None;",
- raise_on_any_unexpected=True,
):
eq_(a1.bs, {None: b2})
@@ -3074,7 +3072,6 @@ class UnpopulatedAttrTest(fixtures.TestBase):
with expect_warnings(
"Attribute keyed dictionary value for attribute "
"'B.a' was None;",
- raise_on_any_unexpected=True,
):
b1.a = a1
else:
diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py
index a3e2f4ef7..db64f91f1 100644
--- a/test/orm/test_deprecations.py
+++ b/test/orm/test_deprecations.py
@@ -414,7 +414,6 @@ class MiscDeprecationsTest(fixtures.TestBase):
with expect_deprecated(
rf"The column_property.{paramname} parameter is deprecated "
r"for column_property\(\)",
- raise_on_any_unexpected=True,
):
column_property(column("q"), **{paramname: value})
@@ -429,7 +428,6 @@ class MiscDeprecationsTest(fixtures.TestBase):
r"for column_property\(\)",
r"The column_property.kw_only parameter is deprecated "
r"for column_property\(\)",
- raise_on_any_unexpected=True,
):
class MyClass(dc_decl_base):
@@ -488,7 +486,8 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
s = addresses.select()
sess = fixture_session()
with testing.expect_deprecated(
- "Implicit coercion of SELECT and " "textual SELECT constructs"
+ "Implicit coercion of SELECT and textual SELECT constructs",
+ "An alias is being generated automatically against joined entity",
):
self.assert_compile(
sess.query(User).join(s, User.addresses),
@@ -1592,7 +1591,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
def go():
with testing.expect_deprecated(
"The AliasOption object is not necessary for entities to be "
- "matched up to a query"
+ "matched up to a query",
):
result = (
q.options(
@@ -1624,7 +1623,8 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
def go():
with testing.expect_deprecated(
- r"Using the Query.instances\(\) method without a context"
+ r"The Query.instances\(\) method is deprecated",
+ r"Using the Query.instances\(\) method without a context",
):
result = list(
q.options(contains_eager(User.addresses)).instances(
@@ -1639,7 +1639,8 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
def go():
with testing.expect_deprecated(
- r"Using the Query.instances\(\) method without a context"
+ r"The Query.instances\(\) method is deprecated",
+ r"Using the Query.instances\(\) method without a context",
):
result = list(
q.options(contains_eager(User.addresses)).instances(
@@ -1674,7 +1675,6 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
r"Using the Query.instances\(\) method without a context",
r"The Query.instances\(\) method is deprecated and will be "
r"removed in a future release.",
- raise_on_any_unexpected=True,
):
result = list(
q.options(
@@ -1721,7 +1721,6 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
r"Using the Query.instances\(\) method without a context",
r"The Query.instances\(\) method is deprecated and will be "
r"removed in a future release.",
- raise_on_any_unexpected=True,
):
result = list(
q.options(
diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py
index ef6d4b684..fa44dbf10 100644
--- a/test/orm/test_eager_relations.py
+++ b/test/orm/test_eager_relations.py
@@ -1656,7 +1656,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
def go():
eq_(u1.addresses[0].user, u1)
- with testing.expect_warnings(raise_on_any_unexpected=True):
+ with testing.expect_warnings():
self.assert_sql_execution(
testing.db,
go,
@@ -1707,7 +1707,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
def go():
eq_(u1.addresses[0].user, u1)
- with testing.expect_warnings(raise_on_any_unexpected=True):
+ with testing.expect_warnings():
self.assert_sql_execution(
testing.db,
go,
diff --git a/test/orm/test_joins.py b/test/orm/test_joins.py
index 3120a160d..ff684d846 100644
--- a/test/orm/test_joins.py
+++ b/test/orm/test_joins.py
@@ -170,7 +170,6 @@ class InheritedJoinTest(InheritedTest, AssertsCompiledSQL):
r"Mapper\[Manager\(managers\)\] due to overlapping",
"An alias is being generated automatically against joined entity "
r"Mapper\[Boss\(boss\)\] due to overlapping",
- raise_on_any_unexpected=True,
):
self.assert_compile(
q,
diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py
index 0cbcc01f3..12651fe36 100644
--- a/test/orm/test_relationships.py
+++ b/test/orm/test_relationships.py
@@ -33,7 +33,6 @@ from sqlalchemy.orm.interfaces import MANYTOONE
from sqlalchemy.orm.interfaces import ONETOMANY
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
-from sqlalchemy.testing import assert_warns_message
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
from sqlalchemy.testing import expect_raises_message
@@ -656,18 +655,15 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
add_bsub2_a_viewonly=False,
add_b_a_overlaps=None,
):
-
Base = self.mapper_registry.generate_base()
class A(Base):
-
__tablename__ = "a"
id = Column(Integer, primary_key=True)
a_members = relationship("AMember", backref="a")
class AMember(Base):
-
__tablename__ = "a_member"
a_id = Column(Integer, ForeignKey("a.id"), primary_key=True)
@@ -707,14 +703,12 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
# however, *no* warning should be emitted otherwise.
class BSub1(B):
-
if add_bsub1_a:
a = relationship("A")
__mapper_args__ = {"polymorphic_identity": "bsub1"}
class BSub2(B):
-
if add_bsub2_a_viewonly:
a = relationship("A", viewonly=True)
@@ -729,7 +723,6 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
return A, AMember, B, BSub1, BSub2
def _fixture_two(self, setup_backrefs=False, setup_overlaps=False):
-
Base = self.mapper_registry.generate_base()
# purposely using the comma to make sure parsing the comma works
@@ -874,15 +867,13 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
@testing.provide_metadata
def test_simple_warn(self):
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship '(?:Child.parent|Parent.children)' will copy "
r"column parent.id to column child.parent_id, which conflicts "
r"with relationship\(s\): '(?:Parent.children|Child.parent)' "
- r"\(copies parent.id to child.parent_id\).",
- self._fixture_two,
- setup_backrefs=False,
- )
+ r"\(copies parent.id to child.parent_id\)."
+ ):
+ self._fixture_two(setup_backrefs=False)
@testing.combinations((True,), (False,), argnames="set_overlaps")
def test_fixture_five(self, metadata, set_overlaps):
@@ -965,15 +956,12 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
@testing.provide_metadata
def test_double_rel_same_mapper_warns(self):
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship 'Parent.child[12]' will copy column parent.id to "
r"column child.parent_id, which conflicts with relationship\(s\): "
- r"'Parent.child[12]' \(copies parent.id to child.parent_id\)",
- self._fixture_three,
- use_same_mappers=True,
- setup_overlaps=False,
- )
+ r"'Parent.child[12]' \(copies parent.id to child.parent_id\)"
+ ):
+ self._fixture_three(use_same_mappers=True, setup_overlaps=False)
@testing.provide_metadata
def test_double_rel_same_mapper_overlaps_works(self):
@@ -985,48 +973,37 @@ class OverlappingFksSiblingTest(fixtures.MappedTest):
@testing.provide_metadata
def test_warn_one(self):
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship '(?:BSub1.a|BSub2.a_member|B.a)' will copy column "
- r"(?:a.id|a_member.a_id) to column b.a_id",
- self._fixture_one,
- add_b_a=True,
- add_bsub1_a=True,
- )
+ r"(?:a.id|a_member.a_id) to column b.a_id"
+ ):
+ self._fixture_one(add_b_a=True, add_bsub1_a=True)
@testing.provide_metadata
def test_warn_two(self):
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship '(?:BSub1.a|B.a_member)' will copy column "
- r"(?:a.id|a_member.a_id) to column b.a_id",
- self._fixture_one,
- add_b_amember=True,
- add_bsub1_a=True,
- )
+ r"(?:a.id|a_member.a_id) to column b.a_id"
+ ):
+ self._fixture_one(add_b_amember=True, add_bsub1_a=True)
@testing.provide_metadata
def test_warn_three(self):
- assert_warns_message(
- exc.SAWarning,
- r"relationship '(?:BSub1.a|B.a_member|B.a)' will copy column "
- r"(?:a.id|a_member.a_id) to column b.a_id",
- self._fixture_one,
- add_b_amember=True,
- add_bsub1_a=True,
- add_b_a=True,
- )
+ with expect_warnings(
+ r"relationship '(?:BSub1.a|B.a_member|BSub2.a_member|B.a)' "
+ r"will copy column (?:a.id|a_member.a_id) to column b.a_id",
+ ):
+ self._fixture_one(
+ add_b_amember=True, add_bsub1_a=True, add_b_a=True
+ )
@testing.provide_metadata
def test_warn_four(self):
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship '(?:B.a|BSub2.a_member|B.a)' will copy column "
- r"(?:a.id|a_member.a_id) to column b.a_id",
- self._fixture_one,
- add_bsub2_a_viewonly=True,
- add_b_a=True,
- )
+ r"(?:a.id|a_member.a_id) to column b.a_id"
+ ):
+ self._fixture_one(add_bsub2_a_viewonly=True, add_b_a=True)
@testing.provide_metadata
def test_works_one(self):
@@ -1303,12 +1280,11 @@ class CompositeSelfRefFKTest(fixtures.MappedTest, AssertsCompiledSQL):
},
)
- assert_warns_message(
- exc.SAWarning,
+ with expect_warnings(
r"relationship .* will copy column .* to column "
- r"employee_t.company_id, which conflicts with relationship\(s\)",
- configure_mappers,
- )
+ r"employee_t.company_id, which conflicts with relationship\(s\)"
+ ):
+ configure_mappers()
def test_annotated_no_overwriting(self):
Employee, Company, employee_t, company_t = (
@@ -2537,7 +2513,6 @@ class JoinConditionErrorTest(fixtures.TestBase):
argnames="argname, arg",
)
def test_invalid_string_args(self, registry, argname, arg):
-
kw = {argname: arg}
Base = registry.generate_base()
@@ -4789,7 +4764,6 @@ class SecondaryNestedJoinTest(
)
def test_render_lazyload(self):
-
A = self.classes.A
sess = fixture_session()
a1 = sess.query(A).filter(A.name == "a1").first()
@@ -5968,7 +5942,6 @@ class InactiveHistoryNoRaiseTest(_fixtures.FixtureTest):
delete,
legacy_inactive_history_style,
):
-
if delete:
assert not backref, "delete and backref are mutually exclusive"
diff --git a/test/orm/test_session.py b/test/orm/test_session.py
index 5a0431788..395603a77 100644
--- a/test/orm/test_session.py
+++ b/test/orm/test_session.py
@@ -36,6 +36,7 @@ from sqlalchemy.testing import config
from sqlalchemy.testing import engines
from sqlalchemy.testing import eq_
from sqlalchemy.testing import expect_raises_message
+from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
from sqlalchemy.testing import is_false
@@ -108,7 +109,6 @@ class ExecutionTest(_fixtures.FixtureTest):
)
def test_no_string_execute(self, connection):
-
with Session(bind=connection) as sess:
with expect_raises_message(
sa.exc.ArgumentError,
@@ -242,7 +242,6 @@ class TransScopingTest(_fixtures.FixtureTest):
self.mapper_registry.map_imperatively(Address, addresses)
with Session(testing.db) as sess:
-
sess.add(User(name="u1"))
sess.commit()
@@ -2293,13 +2292,13 @@ class FlushWarningsTest(fixtures.MappedTest):
def evt(mapper, conn, instance):
instance.addresses.append(Address(email="x1"))
- self._test(evt, "collection append")
+ self._test(evt, "collection append", "related attribute set")
def test_o2m_cascade_remove(self):
def evt(mapper, conn, instance):
del instance.addresses[0]
- self._test(evt, "collection remove")
+ self._test(evt, "collection remove", "related attribute set")
def test_m2o_cascade_add(self):
User = self.classes.User
@@ -2308,14 +2307,19 @@ class FlushWarningsTest(fixtures.MappedTest):
instance.addresses[0].user = User(name="u2")
with expect_raises_message(orm_exc.FlushError, ".*Over 100"):
- self._test(evt, "related attribute set")
+ self._test(
+ evt,
+ "related attribute set",
+ "collection remove",
+ "collection append",
+ )
def test_m2o_cascade_remove(self):
def evt(mapper, conn, instance):
a1 = instance.addresses[0]
del a1.user
- self._test(evt, "related attribute delete")
+ self._test(evt, "related attribute delete", "collection remove")
def test_plain_add(self):
Address = self.classes.Address
@@ -2344,7 +2348,7 @@ class FlushWarningsTest(fixtures.MappedTest):
):
self._test(evt, r"Session.delete\(\)")
- def _test(self, fn, method):
+ def _test(self, fn, *methods):
User = self.classes.User
Address = self.classes.Address
@@ -2353,6 +2357,8 @@ class FlushWarningsTest(fixtures.MappedTest):
u1 = User(name="u1", addresses=[Address(name="a1")])
s.add(u1)
- assert_warns_message(
- sa.exc.SAWarning, "Usage of the '%s'" % method, s.commit
- )
+
+ with expect_warnings(
+ *[f"Usage of the '{method}'" for method in methods]
+ ):
+ s.commit()
diff --git a/test/orm/test_utils.py b/test/orm/test_utils.py
index 3bd0230de..4d6c14863 100644
--- a/test/orm/test_utils.py
+++ b/test/orm/test_utils.py
@@ -78,7 +78,6 @@ class ContextualWarningsTest(fixtures.TestBase):
"bar.foo_id, which conflicts with relationship(s): 'Foo.bars' "
"(copies foo.id to bar.foo_id). "
),
- raise_on_any_unexpected=True,
):
decl_base.registry.configure()
@@ -96,7 +95,6 @@ class ContextualWarningsTest(fixtures.TestBase):
"invoked automatically in response to a user-initiated "
"operation.)"
),
- raise_on_any_unexpected=True,
):
FooAlias = aliased(Foo)
assert hasattr(FooAlias, "bars")
@@ -115,7 +113,6 @@ class ContextualWarningsTest(fixtures.TestBase):
"invoked automatically in response to a user-initiated "
"operation.)"
),
- raise_on_any_unexpected=True,
):
foo = Foo()
assert hasattr(foo, "bars")
@@ -145,7 +142,6 @@ class ContextualWarningsTest(fixtures.TestBase):
"process, which was invoked automatically in response to a "
"user-initiated operation.)"
),
- raise_on_any_unexpected=True,
):
sess.execute(select(Foo))
diff --git a/test/sql/test_delete.py b/test/sql/test_delete.py
index 5b7e5ebbe..9f9e104b6 100644
--- a/test/sql/test_delete.py
+++ b/test/sql/test_delete.py
@@ -76,11 +76,15 @@ class DeleteTest(_DeleteTestBase, fixtures.TablesTest, AssertsCompiledSQL):
def test_where_empty(self):
table1 = self.tables.mytable
- with expect_deprecated():
+ with expect_deprecated(
+ r"Invoking and_\(\) without arguments is deprecated"
+ ):
self.assert_compile(
table1.delete().where(and_()), "DELETE FROM mytable"
)
- with expect_deprecated():
+ with expect_deprecated(
+ r"Invoking or_\(\) without arguments is deprecated"
+ ):
self.assert_compile(
table1.delete().where(or_()), "DELETE FROM mytable"
)
diff --git a/test/sql/test_insert_exec.py b/test/sql/test_insert_exec.py
index f545671e7..0fee9bd35 100644
--- a/test/sql/test_insert_exec.py
+++ b/test/sql/test_insert_exec.py
@@ -1239,7 +1239,6 @@ class IMVSentinelTest(fixtures.TestBase):
):
return expect_warnings(
"Batches were downgraded",
- raise_on_any_unexpected=True,
)
return contextlib.nullcontext()
@@ -1982,7 +1981,6 @@ class IMVSentinelTest(fixtures.TestBase):
):
with expect_warnings(
"Batches were downgraded for sorted INSERT",
- raise_on_any_unexpected=True,
):
result = connection.execute(stmt, data)
else:
@@ -2407,7 +2405,6 @@ class IMVSentinelTest(fixtures.TestBase):
with expect_warnings(
r".*but has no Python-side or server-side default ",
- raise_on_any_unexpected=True,
):
with expect_raises(exc.IntegrityError):
connection.execute(