Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/exc.py: 68%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# exc.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
8"""Exceptions used with SQLAlchemy.
10The base exception class is :exc:`.SQLAlchemyError`. Exceptions which are
11raised as a result of DBAPI exceptions are all subclasses of
12:exc:`.DBAPIError`.
14"""
16from __future__ import annotations
18import typing
19from typing import Any
20from typing import List
21from typing import Optional
22from typing import overload
23from typing import Tuple
24from typing import Type
25from typing import Union
27from .util import compat
28from .util import preloaded as _preloaded
30if typing.TYPE_CHECKING:
31 from .engine.interfaces import _AnyExecuteParams
32 from .engine.interfaces import Dialect
33 from .sql.compiler import Compiled
34 from .sql.compiler import TypeCompiler
35 from .sql.elements import ClauseElement
37if typing.TYPE_CHECKING:
38 _version_token: str
39else:
40 # set by __init__.py
41 _version_token = None
44class HasDescriptionCode:
45 """helper which adds 'code' as an attribute and '_code_str' as a method"""
47 code: Optional[str] = None
49 def __init__(self, *arg: Any, **kw: Any):
50 code = kw.pop("code", None)
51 if code is not None:
52 self.code = code
53 super().__init__(*arg, **kw)
55 _what_are_we = "error"
57 def _code_str(self) -> str:
58 if not self.code:
59 return ""
60 else:
61 return (
62 f"(Background on this {self._what_are_we} at: "
63 f"https://sqlalche.me/e/{_version_token}/{self.code})"
64 )
66 def __str__(self) -> str:
67 message = super().__str__()
68 if self.code:
69 message = "%s %s" % (message, self._code_str())
70 return message
73class SQLAlchemyError(HasDescriptionCode, Exception):
74 """Generic error class."""
76 def _message(self) -> str:
77 # rules:
78 #
79 # 1. single arg string will usually be a unicode
80 # object, but since __str__() must return unicode, check for
81 # bytestring just in case
82 #
83 # 2. for multiple self.args, this is not a case in current
84 # SQLAlchemy though this is happening in at least one known external
85 # library, call str() which does a repr().
86 #
87 text: str
89 if len(self.args) == 1:
90 arg_text = self.args[0]
92 if isinstance(arg_text, bytes):
93 text = compat.decode_backslashreplace(arg_text, "utf-8")
94 # This is for when the argument is not a string of any sort.
95 # Otherwise, converting this exception to string would fail for
96 # non-string arguments.
97 else:
98 text = str(arg_text)
100 return text
101 else:
102 # this is not a normal case within SQLAlchemy but is here for
103 # compatibility with Exception.args - the str() comes out as
104 # a repr() of the tuple
105 return str(self.args)
107 def _sql_message(self) -> str:
108 message = self._message()
110 if self.code:
111 message = "%s %s" % (message, self._code_str())
113 return message
115 def __str__(self) -> str:
116 return self._sql_message()
119class EmulatedDBAPIException(Exception):
120 """Serves as the base of the DBAPI ``Error`` class for dialects where
121 a DBAPI exception hierrchy needs to be emulated.
123 The current example is the asyncpg dialect.
125 .. versionadded:: 2.1
127 """
129 orig: Exception | None
131 def __init__(self, message: str, orig: Exception | None = None):
132 # we accept None for Exception since all DBAPI.Error objects
133 # need to support construction with a message alone
134 super().__init__(message)
135 self.orig = orig
137 @property
138 def driver_exception(self) -> Exception:
139 """The original driver exception that was raised.
141 This exception object will always originate from outside of
142 SQLAlchemy.
144 """
146 if self.orig is None:
147 raise ValueError(
148 "No original exception is present. Was this "
149 "EmulatedDBAPIException constructed without a driver error?"
150 )
151 return self.orig
153 def __reduce__(self) -> Any:
154 return self.__class__, (self.args[0], self.orig)
157class ArgumentError(SQLAlchemyError):
158 """Raised when an invalid or conflicting function argument is supplied.
160 This error generally corresponds to construction time state errors.
162 """
165class DuplicateColumnError(ArgumentError):
166 """a Column is being added to a Table that would replace another
167 Column, without appropriate parameters to allow this in place.
169 .. versionadded:: 2.0.0b4
171 """
174class ObjectNotExecutableError(ArgumentError):
175 """Raised when an object is passed to .execute() that can't be
176 executed as SQL.
178 """
180 def __init__(self, target: Any):
181 super().__init__(f"Not an executable object: {target!r}")
182 self.target = target
184 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
185 return self.__class__, (self.target,)
188class NoSuchModuleError(ArgumentError):
189 """Raised when a dynamically-loaded module (usually a database dialect)
190 of a particular name cannot be located."""
193class NoForeignKeysError(ArgumentError):
194 """Raised when no foreign keys can be located between two selectables
195 during a join."""
198class AmbiguousForeignKeysError(ArgumentError):
199 """Raised when more than one foreign key matching can be located
200 between two selectables during a join."""
203class ConstraintColumnNotFoundError(ArgumentError):
204 """raised when a constraint refers to a string column name that
205 is not present in the table being constrained.
207 .. versionadded:: 2.0
209 """
212class CircularDependencyError(SQLAlchemyError):
213 """Raised by topological sorts when a circular dependency is detected.
215 There are two scenarios where this error occurs:
217 * In a Session flush operation, if two objects are mutually dependent
218 on each other, they can not be inserted or deleted via INSERT or
219 DELETE statements alone; an UPDATE will be needed to post-associate
220 or pre-deassociate one of the foreign key constrained values.
221 The ``post_update`` flag described at :ref:`post_update` can resolve
222 this cycle.
223 * In a :attr:`_schema.MetaData.sorted_tables` operation, two
224 :class:`_schema.ForeignKey`
225 or :class:`_schema.ForeignKeyConstraint` objects mutually refer to each
226 other. Apply the ``use_alter=True`` flag to one or both,
227 see :ref:`use_alter`.
229 """
231 def __init__(
232 self,
233 message: str,
234 cycles: Any,
235 edges: Any,
236 msg: Optional[str] = None,
237 code: Optional[str] = None,
238 ):
239 if msg is None:
240 message += " (%s)" % ", ".join(repr(s) for s in cycles)
241 else:
242 message = msg
243 SQLAlchemyError.__init__(self, message, code=code)
244 self.cycles = cycles
245 self.edges = edges
247 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
248 return (
249 self.__class__,
250 (None, self.cycles, self.edges, self.args[0]),
251 {"code": self.code} if self.code is not None else {},
252 )
255class CompileError(SQLAlchemyError):
256 """Raised when an error occurs during SQL compilation"""
259class UnsupportedCompilationError(CompileError):
260 """Raised when an operation is not supported by the given compiler.
262 .. seealso::
264 :ref:`faq_sql_expression_string`
266 :ref:`error_l7de`
267 """
269 code = "l7de"
271 def __init__(
272 self,
273 compiler: Union[Compiled, TypeCompiler],
274 element_type: Type[ClauseElement],
275 message: Optional[str] = None,
276 ):
277 super().__init__(
278 "Compiler %r can't render element of type %s%s"
279 % (compiler, element_type, ": %s" % message if message else "")
280 )
281 self.compiler = compiler
282 self.element_type = element_type
283 self.message = message
285 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
286 return self.__class__, (self.compiler, self.element_type, self.message)
289class IdentifierError(SQLAlchemyError):
290 """Raised when a schema name is beyond the max character limit"""
293class DisconnectionError(SQLAlchemyError):
294 """A disconnect is detected on a raw DB-API connection.
296 This error is raised and consumed internally by a connection pool. It can
297 be raised by the :meth:`_events.PoolEvents.checkout`
298 event so that the host pool
299 forces a retry; the exception will be caught three times in a row before
300 the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError`
301 regarding the connection attempt.
303 """
305 invalidate_pool: bool = False
308class InvalidatePoolError(DisconnectionError):
309 """Raised when the connection pool should invalidate all stale connections.
311 A subclass of :class:`_exc.DisconnectionError` that indicates that the
312 disconnect situation encountered on the connection probably means the
313 entire pool should be invalidated, as the database has been restarted.
315 This exception will be handled otherwise the same way as
316 :class:`_exc.DisconnectionError`, allowing three attempts to reconnect
317 before giving up.
319 """
321 invalidate_pool: bool = True
324class TimeoutError(SQLAlchemyError): # noqa
325 """Raised when a connection pool times out on getting a connection."""
328class InvalidRequestError(SQLAlchemyError):
329 """SQLAlchemy was asked to do something it can't do.
331 This error generally corresponds to runtime state errors.
333 """
336class IllegalStateChangeError(InvalidRequestError):
337 """An object that tracks state encountered an illegal state change
338 of some kind.
340 .. versionadded:: 2.0
342 """
345class NoInspectionAvailable(InvalidRequestError):
346 """A subject passed to :func:`sqlalchemy.inspection.inspect` produced
347 no context for inspection."""
350class PendingRollbackError(InvalidRequestError):
351 """A transaction has failed and needs to be rolled back before
352 continuing.
354 .. versionadded:: 1.4
356 """
359class ResourceClosedError(InvalidRequestError):
360 """An operation was requested from a connection, cursor, or other
361 object that's in a closed state."""
364class NoSuchColumnError(InvalidRequestError, KeyError):
365 """A nonexistent column is requested from a ``Row``."""
368class AmbiguousColumnError(InvalidRequestError):
369 """Raised when a column/attribute name is ambiguous across multiple
370 entities.
372 This can occur when using :meth:`_sql.Select.filter_by` with multiple
373 joined tables that have columns with the same name.
375 .. versionadded:: 2.1
377 """
380class NoResultFound(InvalidRequestError):
381 """A database result was required but none was found.
384 .. versionchanged:: 1.4 This exception is now part of the
385 ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol
386 remains importable from ``sqlalchemy.orm.exc``.
389 """
392class MultipleResultsFound(InvalidRequestError):
393 """A single database result was required but more than one were found.
395 .. versionchanged:: 1.4 This exception is now part of the
396 ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol
397 remains importable from ``sqlalchemy.orm.exc``.
400 """
403class NoReferenceError(InvalidRequestError):
404 """Raised by ``ForeignKey`` to indicate a reference cannot be resolved."""
406 table_name: str
409class AwaitRequired(InvalidRequestError):
410 """Error raised by the async greenlet spawn if no async operation
411 was awaited when it required one.
413 """
415 code = "xd1r"
418class MissingGreenlet(InvalidRequestError):
419 r"""Error raised by the async greenlet await\_ if called while not inside
420 the greenlet spawn context.
422 """
424 code = "xd2s"
427class NoReferencedTableError(NoReferenceError):
428 """Raised by ``ForeignKey`` when the referred ``Table`` cannot be
429 located.
431 """
433 def __init__(self, message: str, tname: str):
434 NoReferenceError.__init__(self, message)
435 self.table_name = tname
437 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
438 return self.__class__, (self.args[0], self.table_name)
441class NoReferencedColumnError(NoReferenceError):
442 """Raised by ``ForeignKey`` when the referred ``Column`` cannot be
443 located.
445 """
447 def __init__(self, message: str, tname: str, cname: str):
448 NoReferenceError.__init__(self, message)
449 self.table_name = tname
450 self.column_name = cname
452 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
453 return (
454 self.__class__,
455 (self.args[0], self.table_name, self.column_name),
456 )
459class NoSuchTableError(InvalidRequestError):
460 """Table does not exist or is not visible to a connection."""
463class UnreflectableTableError(InvalidRequestError):
464 """Table exists but can't be reflected for some reason."""
467class UnboundExecutionError(InvalidRequestError):
468 """SQL was attempted without a database connection to execute it on."""
471class DontWrapMixin:
472 """A mixin class which, when applied to a user-defined Exception class,
473 will not be wrapped inside of :exc:`.StatementError` if the error is
474 emitted within the process of executing a statement.
476 E.g.::
478 from sqlalchemy.exc import DontWrapMixin
481 class MyCustomException(Exception, DontWrapMixin):
482 pass
485 class MySpecialType(TypeDecorator):
486 impl = String
488 def process_bind_param(self, value, dialect):
489 if value == "invalid":
490 raise MyCustomException("invalid!")
492 """
495class StatementError(SQLAlchemyError):
496 """An error occurred during execution of a SQL statement.
498 :class:`StatementError` wraps the exception raised
499 during execution, and features :attr:`.statement`
500 and :attr:`.params` attributes which supply context regarding
501 the specifics of the statement which had an issue.
503 The wrapped exception object is available in
504 the :attr:`.orig` attribute.
506 """
508 statement: Optional[str] = None
509 """The string SQL statement being invoked when this exception occurred."""
511 params: Optional[_AnyExecuteParams] = None
512 """The parameter list being used when this exception occurred."""
514 orig: Optional[BaseException] = None
515 """The original exception that was thrown.
517 .. seealso::
519 :attr:`.DBAPIError.driver_exception` - a more specific attribute that
520 is guaranteed to return the exception object raised by the third
521 party driver in use, even when using asyncio.
523 """
525 ismulti: Optional[bool] = None
526 """multi parameter passed to repr_params(). None is meaningful."""
528 connection_invalidated: bool = False
530 def __init__(
531 self,
532 message: str,
533 statement: Optional[str],
534 params: Optional[_AnyExecuteParams],
535 orig: Optional[BaseException],
536 hide_parameters: bool = False,
537 code: Optional[str] = None,
538 ismulti: Optional[bool] = None,
539 ):
540 SQLAlchemyError.__init__(self, message, code=code)
541 self.statement = statement
542 self.params = params
543 self.orig = orig
544 self.ismulti = ismulti
545 self.hide_parameters = hide_parameters
546 self.detail: List[str] = []
548 def add_detail(self, msg: str) -> None:
549 self.detail.append(msg)
551 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
552 return (
553 self.__class__,
554 (
555 self.args[0],
556 self.statement,
557 self.params,
558 self.orig,
559 self.hide_parameters,
560 self.__dict__.get("code"),
561 self.ismulti,
562 ),
563 {"detail": self.detail},
564 )
566 @_preloaded.preload_module("sqlalchemy.sql.util")
567 def _sql_message(self) -> str:
568 util = _preloaded.sql_util
570 details = [self._message()]
571 if self.statement:
572 stmt_detail = "[SQL: %s]" % self.statement
573 details.append(stmt_detail)
574 if self.params:
575 if self.hide_parameters:
576 details.append(
577 "[SQL parameters hidden due to hide_parameters=True]"
578 )
579 else:
580 params_repr = util._repr_params(
581 self.params, 10, ismulti=self.ismulti
582 )
583 details.append("[parameters: %r]" % params_repr)
584 code_str = self._code_str()
585 if code_str:
586 details.append(code_str)
587 return "\n".join(["(%s)" % det for det in self.detail] + details)
590class DBAPIError(StatementError):
591 """Raised when the execution of a database operation fails.
593 Wraps exceptions raised by the DB-API underlying the
594 database operation. Driver-specific implementations of the standard
595 DB-API exception types are wrapped by matching sub-types of SQLAlchemy's
596 :class:`DBAPIError` when possible. DB-API's ``Error`` type maps to
597 :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical. Note
598 that there is no guarantee that different DB-API implementations will
599 raise the same exception type for any given error condition.
601 :class:`DBAPIError` features :attr:`~.StatementError.statement`
602 and :attr:`~.StatementError.params` attributes which supply context
603 regarding the specifics of the statement which had an issue, for the
604 typical case when the error was raised within the context of
605 emitting a SQL statement.
607 The wrapped exception object is available in the
608 :attr:`~.StatementError.orig` attribute. Its type and properties are
609 DB-API implementation specific.
611 """
613 code = "dbapi"
615 orig: Optional[Exception]
617 @overload
618 @classmethod
619 def instance(
620 cls,
621 statement: Optional[str],
622 params: Optional[_AnyExecuteParams],
623 orig: Exception,
624 dbapi_base_err: Type[Exception],
625 hide_parameters: bool = False,
626 connection_invalidated: bool = False,
627 dialect: Optional[Dialect] = None,
628 ismulti: Optional[bool] = None,
629 ) -> StatementError: ...
631 @overload
632 @classmethod
633 def instance(
634 cls,
635 statement: Optional[str],
636 params: Optional[_AnyExecuteParams],
637 orig: DontWrapMixin,
638 dbapi_base_err: Type[Exception],
639 hide_parameters: bool = False,
640 connection_invalidated: bool = False,
641 dialect: Optional[Dialect] = None,
642 ismulti: Optional[bool] = None,
643 ) -> DontWrapMixin: ...
645 @overload
646 @classmethod
647 def instance(
648 cls,
649 statement: Optional[str],
650 params: Optional[_AnyExecuteParams],
651 orig: BaseException,
652 dbapi_base_err: Type[Exception],
653 hide_parameters: bool = False,
654 connection_invalidated: bool = False,
655 dialect: Optional[Dialect] = None,
656 ismulti: Optional[bool] = None,
657 ) -> BaseException: ...
659 @classmethod
660 def instance(
661 cls,
662 statement: Optional[str],
663 params: Optional[_AnyExecuteParams],
664 orig: Union[BaseException, DontWrapMixin],
665 dbapi_base_err: Type[Exception],
666 hide_parameters: bool = False,
667 connection_invalidated: bool = False,
668 dialect: Optional[Dialect] = None,
669 ismulti: Optional[bool] = None,
670 ) -> Union[BaseException, DontWrapMixin]:
671 # Don't ever wrap these, just return them directly as if
672 # DBAPIError didn't exist.
673 if (
674 isinstance(orig, BaseException) and not isinstance(orig, Exception)
675 ) or isinstance(orig, DontWrapMixin):
676 return orig
678 if orig is not None:
679 # not a DBAPI error, statement is present.
680 # raise a StatementError
681 if isinstance(orig, SQLAlchemyError) and statement:
682 return StatementError(
683 "(%s.%s) %s"
684 % (
685 orig.__class__.__module__,
686 orig.__class__.__name__,
687 orig.args[0],
688 ),
689 statement,
690 params,
691 orig,
692 hide_parameters=hide_parameters,
693 code=orig.code,
694 ismulti=ismulti,
695 )
696 elif not isinstance(orig, dbapi_base_err) and statement:
697 return StatementError(
698 "(%s.%s) %s"
699 % (
700 orig.__class__.__module__,
701 orig.__class__.__name__,
702 orig,
703 ),
704 statement,
705 params,
706 orig,
707 hide_parameters=hide_parameters,
708 ismulti=ismulti,
709 )
711 glob = globals()
712 for super_ in orig.__class__.__mro__:
713 name = super_.__name__
714 if dialect:
715 name = dialect.dbapi_exception_translation_map.get(
716 name, name
717 )
718 if name in glob and issubclass(glob[name], DBAPIError):
719 cls = glob[name]
720 break
722 return cls(
723 statement,
724 params,
725 orig,
726 connection_invalidated=connection_invalidated,
727 hide_parameters=hide_parameters,
728 code=cls.code,
729 ismulti=ismulti,
730 )
732 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
733 return (
734 self.__class__,
735 (
736 self.statement,
737 self.params,
738 self.orig,
739 self.hide_parameters,
740 self.connection_invalidated,
741 self.__dict__.get("code"),
742 self.ismulti,
743 ),
744 {"detail": self.detail},
745 )
747 def __init__(
748 self,
749 statement: Optional[str],
750 params: Optional[_AnyExecuteParams],
751 orig: BaseException,
752 hide_parameters: bool = False,
753 connection_invalidated: bool = False,
754 code: Optional[str] = None,
755 ismulti: Optional[bool] = None,
756 ):
757 try:
758 text = str(orig)
759 except Exception as e:
760 text = "Error in str() of DB-API-generated exception: " + str(e)
761 StatementError.__init__(
762 self,
763 "(%s.%s) %s"
764 % (orig.__class__.__module__, orig.__class__.__name__, text),
765 statement,
766 params,
767 orig,
768 hide_parameters,
769 code=code,
770 ismulti=ismulti,
771 )
772 self.connection_invalidated = connection_invalidated
774 @property
775 def driver_exception(self) -> Exception:
776 """The exception object originating from the driver (DBAPI) outside
777 of SQLAlchemy.
779 In the case of some asyncio dialects, special steps are taken to
780 resolve the exception to what the third party driver has raised, even
781 for SQLAlchemy dialects that include an "emulated" DBAPI exception
782 hierarchy.
784 For non-asyncio dialects, this attribute will be the same attribute
785 as the :attr:`.StatementError.orig` attribute.
787 For an asyncio dialect provided by SQLAlchemy, depending on if the
788 dialect provides an "emulated" exception hierarchy or if the underlying
789 DBAPI raises DBAPI-style exceptions, it will refer to either the
790 :attr:`.EmulatedDBAPIException.driver_exception` attribute on the
791 :class:`.EmulatedDBAPIException` that's thrown (such as when using
792 asyncpg), or to the actual exception object thrown by the
793 third party driver.
795 .. versionadded:: 2.1
797 """
799 if self.orig is None:
800 raise ValueError(
801 "No original exception is present. Was this "
802 "DBAPIError constructed without a driver error?"
803 )
805 if isinstance(self.orig, EmulatedDBAPIException):
806 return self.orig.driver_exception
807 else:
808 return self.orig
811class InterfaceError(DBAPIError):
812 """Wraps a DB-API InterfaceError."""
814 code = "rvf5"
817class DatabaseError(DBAPIError):
818 """Wraps a DB-API DatabaseError."""
820 code = "4xp6"
823class DataError(DatabaseError):
824 """Wraps a DB-API DataError."""
826 code = "9h9h"
829class OperationalError(DatabaseError):
830 """Wraps a DB-API OperationalError."""
832 code = "e3q8"
835class IntegrityError(DatabaseError):
836 """Wraps a DB-API IntegrityError."""
838 code = "gkpj"
841class InternalError(DatabaseError):
842 """Wraps a DB-API InternalError."""
844 code = "2j85"
847class ProgrammingError(DatabaseError):
848 """Wraps a DB-API ProgrammingError."""
850 code = "f405"
853class NotSupportedError(DatabaseError):
854 """Wraps a DB-API NotSupportedError."""
856 code = "tw8g"
859# Warnings
862class SATestSuiteWarning(Warning):
863 """warning for a condition detected during tests that is non-fatal
865 Currently outside of SAWarning so that we can work around tools like
866 Alembic doing the wrong thing with warnings.
868 """
871class SADeprecationWarning(HasDescriptionCode, DeprecationWarning):
872 """Issued for usage of deprecated APIs."""
874 deprecated_since: Optional[str] = None
875 "Indicates the version that started raising this deprecation warning"
878class Base20DeprecationWarning(SADeprecationWarning):
879 """Issued for usage of APIs specifically deprecated or legacy in
880 SQLAlchemy 2.0.
882 .. seealso::
884 :ref:`error_b8d9`.
886 :ref:`deprecation_20_mode`
888 """
890 deprecated_since: Optional[str] = "1.4"
891 "Indicates the version that started raising this deprecation warning"
893 def __str__(self) -> str:
894 return (
895 super().__str__()
896 + " (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)"
897 )
900class LegacyAPIWarning(Base20DeprecationWarning):
901 """indicates an API that is in 'legacy' status, a long term deprecation."""
904class MovedIn20Warning(Base20DeprecationWarning):
905 """Subtype of Base20DeprecationWarning to indicate an API that moved
906 only.
907 """
910class SAPendingDeprecationWarning(PendingDeprecationWarning):
911 """A similar warning as :class:`_exc.SADeprecationWarning`, this warning
912 is not used in modern versions of SQLAlchemy.
914 """
916 deprecated_since: Optional[str] = None
917 "Indicates the version that started raising this deprecation warning"
920class SAWarning(HasDescriptionCode, RuntimeWarning):
921 """Issued at runtime."""
923 _what_are_we = "warning"