]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add support for 'code' in deprecation warnings
authorStephen Rosen <sirosen@globus.org>
Mon, 12 Apr 2021 17:57:17 +0000 (17:57 +0000)
committerStephen Rosen <sirosen@globus.org>
Fri, 7 May 2021 15:59:59 +0000 (15:59 +0000)
SADeprecationWarning and SQLAlchemyError now share 'code' and
'_code_str' through a small mixin/helper class.

In the case of the deprecation warnings, the code_str is injected into
`__str__`, which means the message will be built when a warning is
emitted. An alternative approach would be to build a distinct message
when the warning is instantiated.

`s9r1` is a new error code which points directly to the documentation
on backref cascades, and this is now in place as the code for
`cascade_backrefs` warnings.

Fixes: #6148
doc/build/orm/cascades.rst
lib/sqlalchemy/exc.py
lib/sqlalchemy/orm/unitofwork.py
lib/sqlalchemy/util/deprecations.py

index 4d664476f50e8ad7f3e17217410a8ea1d8dc34e7..716de0b6e3cdafaa8187fdd75ffe674e4442062d 100644 (file)
@@ -555,6 +555,8 @@ operation should be propagated down to referred objects.
 
 .. _backref_cascade:
 
+.. _error_s9r1:
+
 Controlling Cascade on Backrefs
 -------------------------------
 
index a0a86826d17d182c33f1ca6b7989612dcebd4ba0..266294afa0ce6a202ef5154577016a3edcdc125f 100644 (file)
@@ -19,8 +19,8 @@ from .util import compat
 _version_token = None
 
 
-class SQLAlchemyError(Exception):
-    """Generic error class."""
+class _CodeStrMixin(object):
+    """helper which adds 'code' as an attribute and '_code_str' as a method"""
 
     code = None
 
@@ -28,7 +28,7 @@ class SQLAlchemyError(Exception):
         code = kw.pop("code", None)
         if code is not None:
             self.code = code
-        super(SQLAlchemyError, self).__init__(*arg, **kw)
+        super(_CodeStrMixin, self).__init__(*arg, **kw)
 
     def _code_str(self):
         if not self.code:
@@ -43,6 +43,10 @@ class SQLAlchemyError(Exception):
                 )
             )
 
+
+class SQLAlchemyError(_CodeStrMixin, Exception):
+    """Generic error class."""
+
     def _message(self, as_unicode=compat.py3k):
         # rules:
         #
@@ -650,12 +654,18 @@ class NotSupportedError(DatabaseError):
 # Warnings
 
 
-class SADeprecationWarning(DeprecationWarning):
+class SADeprecationWarning(_CodeStrMixin, DeprecationWarning):
     """Issued for usage of deprecated APIs."""
 
     deprecated_since = None
     "Indicates the version that started raising this deprecation warning"
 
+    def __str__(self):
+        message = super(SADeprecationWarning, self).__str__()
+        if self.code:
+            message = "%s %s" % (message, self._code_str())
+        return message
+
 
 class RemovedIn20Warning(SADeprecationWarning):
     """Issued for usage of APIs specifically deprecated in SQLAlchemy 2.0.
index f2b5cf75f88f74c00dcb7277c052d8c095be896f..77bbb47510148b6aa20c6f7109a195bd8dc24574 100644 (file)
@@ -28,7 +28,8 @@ def _warn_for_cascade_backrefs(state, prop):
         "reverse cascade will not take place.  Set cascade_backrefs to "
         "False in either the relationship() or backref() function for "
         "the 2.0 behavior; or to set globally for the whole "
-        "Session, set the future=True flag" % (state.class_.__name__, prop)
+        "Session, set the future=True flag" % (state.class_.__name__, prop),
+        code="s9r1",
     )
 
 
index 5d55a3ae611d5312802e220c3c9744781d86dcf7..1369d61dce429b9a0121131bb5a5b49bef3513d4 100644 (file)
@@ -26,7 +26,7 @@ if os.getenv("SQLALCHEMY_WARN_20", "false").lower() in ("true", "yes", "1"):
     SQLALCHEMY_WARN_20 = True
 
 
-def _warn_with_version(msg, version, type_, stacklevel):
+def _warn_with_version(msg, version, type_, stacklevel, code=None):
     is_20 = issubclass(type_, exc.RemovedIn20Warning)
 
     if is_20 and not SQLALCHEMY_WARN_20:
@@ -35,33 +35,38 @@ def _warn_with_version(msg, version, type_, stacklevel):
     if is_20:
         msg += " (Background on SQLAlchemy 2.0 at: http://sqlalche.me/e/b8d9)"
 
-    warn = type_(msg)
+    warn = type_(msg, code=code)
     warn.deprecated_since = version
 
     warnings.warn(warn, stacklevel=stacklevel + 1)
 
 
-def warn_deprecated(msg, version, stacklevel=3):
-    _warn_with_version(msg, version, exc.SADeprecationWarning, stacklevel)
+def warn_deprecated(msg, version, stacklevel=3, code=None):
+    _warn_with_version(
+        msg, version, exc.SADeprecationWarning, stacklevel, code=code
+    )
 
 
-def warn_deprecated_limited(msg, args, version, stacklevel=3):
+def warn_deprecated_limited(msg, args, version, stacklevel=3, code=None):
     """Issue a deprecation warning with a parameterized string,
     limiting the number of registrations.
 
     """
     if args:
         msg = _hash_limit_string(msg, 10, args)
-    _warn_with_version(msg, version, exc.SADeprecationWarning, stacklevel)
+    _warn_with_version(
+        msg, version, exc.SADeprecationWarning, stacklevel, code=code
+    )
 
 
-def warn_deprecated_20(msg, stacklevel=3):
+def warn_deprecated_20(msg, stacklevel=3, code=None):
 
     _warn_with_version(
         msg,
         exc.RemovedIn20Warning.deprecated_since,
         exc.RemovedIn20Warning,
         stacklevel,
+        code=code,
     )