summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorjonathan vanasco <jonathan@2xlp.com>2021-11-12 12:45:54 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2023-01-24 22:03:06 -0500
commit03212e5aefb3d5c912d25274f447eb9c493f7268 (patch)
treefdf6b7919dab70e979304f43b6fbde9b8ad6515b /lib/sqlalchemy
parent55b243a3f22f267c829f9088fd4e801f0b621a31 (diff)
downloadsqlalchemy-03212e5aefb3d5c912d25274f447eb9c493f7268.tar.gz
add context for warnings emitted from configure_mappers(), autoflush()
Improved the notification of warnings that are emitted within the configure mappers or flush process, which are often invoked as part of a different operation, to add additional context to the message that indicates one of these operations as the source of the warning within operations that may not be obviously related. Fixes: #7305 Change-Id: I79da7a6a5d4cf67d57615d0ffc2b8d8454011c84
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/mapper.py6
-rw-r--r--lib/sqlalchemy/orm/session.py6
-rw-r--r--lib/sqlalchemy/util/langhelpers.py47
3 files changed, 52 insertions, 7 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 141b5c27a..dd3aef8d6 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2311,6 +2311,12 @@ class Mapper(
"columns get mapped." % (key, self, column.key, prop)
)
+ @util.langhelpers.tag_method_for_warnings(
+ "This warning originated from the `configure_mappers()` process, "
+ "which was invoked automatically in response to a user-initiated "
+ "operation.",
+ sa_exc.SAWarning,
+ )
def _check_configure(self) -> None:
if self.registry._new_mappers:
_configure_registries({self.registry}, cascade=True)
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 5bcb22a08..06e84dbcf 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -2872,6 +2872,12 @@ class Session(_SessionClassMethods, EventTarget):
finally:
self.autoflush = autoflush
+ @util.langhelpers.tag_method_for_warnings(
+ "This warning originated from the Session 'autoflush' process, "
+ "which was invoked automatically in response to a user-initiated "
+ "operation.",
+ sa_exc.SAWarning,
+ )
def _autoflush(self) -> None:
if self.autoflush and not self._flushing:
try:
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index 767148045..01f083bec 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -24,6 +24,7 @@ import sys
import textwrap
import threading
import types
+from types import CodeType
from typing import Any
from typing import Callable
from typing import cast
@@ -1825,6 +1826,19 @@ def warn_limited(msg: str, args: Sequence[Any]) -> None:
_warnings_warn(msg, exc.SAWarning)
+_warning_tags: Dict[CodeType, Tuple[str, Type[Warning]]] = {}
+
+
+def tag_method_for_warnings(
+ message: str, category: Type[Warning]
+) -> Callable[[_F], _F]:
+ def go(fn):
+ _warning_tags[fn.__code__] = (message, category)
+ return fn
+
+ return go
+
+
_not_sa_pattern = re.compile(r"^(?:sqlalchemy\.(?!testing)|alembic\.)")
@@ -1846,14 +1860,33 @@ def _warnings_warn(
# ok, but don't crash
stacklevel = 0
else:
- # using __name__ here requires that we have __name__ in the
- # __globals__ of the decorated string functions we make also.
- # we generate this using {"__name__": fn.__module__}
- while frame is not None and re.match(
- _not_sa_pattern, frame.f_globals.get("__name__", "")
- ):
+ stacklevel_found = warning_tag_found = False
+ while frame is not None:
+ # using __name__ here requires that we have __name__ in the
+ # __globals__ of the decorated string functions we make also.
+ # we generate this using {"__name__": fn.__module__}
+ if not stacklevel_found and not re.match(
+ _not_sa_pattern, frame.f_globals.get("__name__", "")
+ ):
+ # stop incrementing stack level if an out-of-SQLA line
+ # were found.
+ stacklevel_found = True
+
+ # however, for the warning tag thing, we have to keep
+ # scanning up the whole traceback
+
+ if frame.f_code in _warning_tags:
+ warning_tag_found = True
+ (_prefix, _category) = _warning_tags[frame.f_code]
+ category = category or _category
+ message = f"{message} ({_prefix})"
+
frame = frame.f_back # type: ignore[assignment]
- stacklevel += 1
+
+ if not stacklevel_found:
+ stacklevel += 1
+ elif stacklevel_found and warning_tag_found:
+ break
if category is not None:
warnings.warn(message, category, stacklevel=stacklevel + 1)