summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util/langhelpers.py
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/util/langhelpers.py
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/util/langhelpers.py')
-rw-r--r--lib/sqlalchemy/util/langhelpers.py47
1 files changed, 40 insertions, 7 deletions
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)