From: Stephen Rosen Date: Mon, 12 Apr 2021 17:57:17 +0000 (+0000) Subject: Add support for 'code' in deprecation warnings X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=824a7890a09f495e05ea5acd8f37b7dc4f1e404d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add support for 'code' in deprecation warnings 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 --- diff --git a/doc/build/orm/cascades.rst b/doc/build/orm/cascades.rst index 4d664476f5..716de0b6e3 100644 --- a/doc/build/orm/cascades.rst +++ b/doc/build/orm/cascades.rst @@ -555,6 +555,8 @@ operation should be propagated down to referred objects. .. _backref_cascade: +.. _error_s9r1: + Controlling Cascade on Backrefs ------------------------------- diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py index a0a86826d1..266294afa0 100644 --- a/lib/sqlalchemy/exc.py +++ b/lib/sqlalchemy/exc.py @@ -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. diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index f2b5cf75f8..77bbb47510 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -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", ) diff --git a/lib/sqlalchemy/util/deprecations.py b/lib/sqlalchemy/util/deprecations.py index 5d55a3ae61..1369d61dce 100644 --- a/lib/sqlalchemy/util/deprecations.py +++ b/lib/sqlalchemy/util/deprecations.py @@ -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, )