codekingpro/portable-devtools
115k
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
7
8"""Exceptions used with SQLAlchemy.
9
10The base exception class is :exc:`.SQLAlchemyError`. Exceptions which are
11raised as a result of DBAPI exceptions are all subclasses of
12:exc:`.DBAPIError`.
13
14"""
15from __future__ import annotations
16
17import typing
18from typing import Any
19from typing import List
20from typing import Optional
21from typing import overload
22from typing import Tuple
23from typing import Type
24from typing import Union
25
26from .util import compat
27from .util import preloaded as _preloaded
28
29if typing.TYPE_CHECKING:
30 from .engine.interfaces import _AnyExecuteParams
31 from .engine.interfaces import Dialect
32 from .sql.compiler import Compiled
33 from .sql.compiler import TypeCompiler
34 from .sql.elements import ClauseElement
35
36if typing.TYPE_CHECKING:
37 _version_token: str
38else:
39 # set by __init__.py
40 _version_token = None
41
42
43class HasDescriptionCode:
44 """helper which adds 'code' as an attribute and '_code_str' as a method"""
45
46 code: Optional[str] = None
47
48 def __init__(self, *arg: Any, **kw: Any):
49 code = kw.pop("code", None)
50 if code is not None:
51 self.code = code
52 super().__init__(*arg, **kw)
53
54 _what_are_we = "error"
55
56 def _code_str(self) -> str:
57 if not self.code:
58 return ""
59 else:
60 return (
61 f"(Background on this {self._what_are_we} at: "
62 f"https://sqlalche.me/e/{_version_token}/{self.code})"
63 )
64
65 def __str__(self) -> str:
66 message = super().__str__()
67 if self.code:
68 message = "%s %s" % (message, self._code_str())
69 return message
70
71
72class SQLAlchemyError(HasDescriptionCode, Exception):
73 """Generic error class."""
74
75 def _message(self) -> str:
76 # rules:
77 #
78 # 1. single arg string will usually be a unicode
79 # object, but since __str__() must return unicode, check for
80 # bytestring just in case
81 #
82 # 2. for multiple self.args, this is not a case in current
83 # SQLAlchemy though this is happening in at least one known external
84 # library, call str() which does a repr().
85 #
86 text: str
87
88 if len(self.args) == 1:
89 arg_text = self.args[0]
90
91 if isinstance(arg_text, bytes):
92 text = compat.decode_backslashreplace(arg_text, "utf-8")
93 # This is for when the argument is not a string of any sort.
94 # Otherwise, converting this exception to string would fail for
95 # non-string arguments.
96 else:
97 text = str(arg_text)
98
99 return text
100 else:
101 # this is not a normal case within SQLAlchemy but is here for
102 # compatibility with Exception.args - the str() comes out as
103 # a repr() of the tuple
104 return str(self.args)
105
106 def _sql_message(self) -> str:
107 message = self._message()
108
109 if self.code:
110 message = "%s %s" % (message, self._code_str())
111
112 return message
113
114 def __str__(self) -> str:
115 return self._sql_message()
116
117
118class ArgumentError(SQLAlchemyError):
119 """Raised when an invalid or conflicting function argument is supplied.
120
121 This error generally corresponds to construction time state errors.
122
123 """
124
125
126class DuplicateColumnError(ArgumentError):
127 """a Column is being added to a Table that would replace another
128 Column, without appropriate parameters to allow this in place.
129
130 .. versionadded:: 2.0.0b4
131
132 """
133
134
135class ObjectNotExecutableError(ArgumentError):
136 """Raised when an object is passed to .execute() that can't be
137 executed as SQL.
138
139 """
140
141 def __init__(self, target: Any):
142 super().__init__("Not an executable object: %r" % target)
143 self.target = target
144
145 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
146 return self.__class__, (self.target,)
147
148
149class NoSuchModuleError(ArgumentError):
150 """Raised when a dynamically-loaded module (usually a database dialect)
151 of a particular name cannot be located."""
152
153
154class NoForeignKeysError(ArgumentError):
155 """Raised when no foreign keys can be located between two selectables
156 during a join."""
157
158
159class AmbiguousForeignKeysError(ArgumentError):
160 """Raised when more than one foreign key matching can be located
161 between two selectables during a join."""
162
163
164class ConstraintColumnNotFoundError(ArgumentError):
165 """raised when a constraint refers to a string column name that
166 is not present in the table being constrained.
167
168 .. versionadded:: 2.0
169
170 """
171
172
173class CircularDependencyError(SQLAlchemyError):
174 """Raised by topological sorts when a circular dependency is detected.
175
176 There are two scenarios where this error occurs:
177
178 * In a Session flush operation, if two objects are mutually dependent
179 on each other, they can not be inserted or deleted via INSERT or
180 DELETE statements alone; an UPDATE will be needed to post-associate
181 or pre-deassociate one of the foreign key constrained values.
182 The ``post_update`` flag described at :ref:`post_update` can resolve
183 this cycle.
184 * In a :attr:`_schema.MetaData.sorted_tables` operation, two
185 :class:`_schema.ForeignKey`
186 or :class:`_schema.ForeignKeyConstraint` objects mutually refer to each
187 other. Apply the ``use_alter=True`` flag to one or both,
188 see :ref:`use_alter`.
189
190 """
191
192 def __init__(
193 self,
194 message: str,
195 cycles: Any,
196 edges: Any,
197 msg: Optional[str] = None,
198 code: Optional[str] = None,
199 ):
200 if msg is None:
201 message += " (%s)" % ", ".join(repr(s) for s in cycles)
202 else:
203 message = msg
204 SQLAlchemyError.__init__(self, message, code=code)
205 self.cycles = cycles
206 self.edges = edges
207
208 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
209 return (
210 self.__class__,
211 (None, self.cycles, self.edges, self.args[0]),
212 {"code": self.code} if self.code is not None else {},
213 )
214
215
216class CompileError(SQLAlchemyError):
217 """Raised when an error occurs during SQL compilation"""
218
219
220class UnsupportedCompilationError(CompileError):
221 """Raised when an operation is not supported by the given compiler.
222
223 .. seealso::
224
225 :ref:`faq_sql_expression_string`
226
227 :ref:`error_l7de`
228 """
229
230 code = "l7de"
231
232 def __init__(
233 self,
234 compiler: Union[Compiled, TypeCompiler],
235 element_type: Type[ClauseElement],
236 message: Optional[str] = None,
237 ):
238 super().__init__(
239 "Compiler %r can't render element of type %s%s"
240 % (compiler, element_type, ": %s" % message if message else "")
241 )
242 self.compiler = compiler
243 self.element_type = element_type
244 self.message = message
245
246 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
247 return self.__class__, (self.compiler, self.element_type, self.message)
248
249
250class IdentifierError(SQLAlchemyError):
251 """Raised when a schema name is beyond the max character limit"""
252
253
254class DisconnectionError(SQLAlchemyError):
255 """A disconnect is detected on a raw DB-API connection.
256
257 This error is raised and consumed internally by a connection pool. It can
258 be raised by the :meth:`_events.PoolEvents.checkout`
259 event so that the host pool
260 forces a retry; the exception will be caught three times in a row before
261 the pool gives up and raises :class:`~sqlalchemy.exc.InvalidRequestError`
262 regarding the connection attempt.
263
264 """
265
266 invalidate_pool: bool = False
267
268
269class InvalidatePoolError(DisconnectionError):
270 """Raised when the connection pool should invalidate all stale connections.
271
272 A subclass of :class:`_exc.DisconnectionError` that indicates that the
273 disconnect situation encountered on the connection probably means the
274 entire pool should be invalidated, as the database has been restarted.
275
276 This exception will be handled otherwise the same way as
277 :class:`_exc.DisconnectionError`, allowing three attempts to reconnect
278 before giving up.
279
280 .. versionadded:: 1.2
281
282 """
283
284 invalidate_pool: bool = True
285
286
287class TimeoutError(SQLAlchemyError): # noqa
288 """Raised when a connection pool times out on getting a connection."""
289
290
291class InvalidRequestError(SQLAlchemyError):
292 """SQLAlchemy was asked to do something it can't do.
293
294 This error generally corresponds to runtime state errors.
295
296 """
297
298
299class IllegalStateChangeError(InvalidRequestError):
300 """An object that tracks state encountered an illegal state change
301 of some kind.
302
303 .. versionadded:: 2.0
304
305 """
306
307
308class NoInspectionAvailable(InvalidRequestError):
309 """A subject passed to :func:`sqlalchemy.inspection.inspect` produced
310 no context for inspection."""
311
312
313class PendingRollbackError(InvalidRequestError):
314 """A transaction has failed and needs to be rolled back before
315 continuing.
316
317 .. versionadded:: 1.4
318
319 """
320
321
322class ResourceClosedError(InvalidRequestError):
323 """An operation was requested from a connection, cursor, or other
324 object that's in a closed state."""
325
326
327class NoSuchColumnError(InvalidRequestError, KeyError):
328 """A nonexistent column is requested from a ``Row``."""
329
330
331class NoResultFound(InvalidRequestError):
332 """A database result was required but none was found.
333
334
335 .. versionchanged:: 1.4 This exception is now part of the
336 ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol
337 remains importable from ``sqlalchemy.orm.exc``.
338
339
340 """
341
342
343class MultipleResultsFound(InvalidRequestError):
344 """A single database result was required but more than one were found.
345
346 .. versionchanged:: 1.4 This exception is now part of the
347 ``sqlalchemy.exc`` module in Core, moved from the ORM. The symbol
348 remains importable from ``sqlalchemy.orm.exc``.
349
350
351 """
352
353
354class NoReferenceError(InvalidRequestError):
355 """Raised by ``ForeignKey`` to indicate a reference cannot be resolved."""
356
357 table_name: str
358
359
360class AwaitRequired(InvalidRequestError):
361 """Error raised by the async greenlet spawn if no async operation
362 was awaited when it required one.
363
364 """
365
366 code = "xd1r"
367
368
369class MissingGreenlet(InvalidRequestError):
370 r"""Error raised by the async greenlet await\_ if called while not inside
371 the greenlet spawn context.
372
373 """
374
375 code = "xd2s"
376
377
378class NoReferencedTableError(NoReferenceError):
379 """Raised by ``ForeignKey`` when the referred ``Table`` cannot be
380 located.
381
382 """
383
384 def __init__(self, message: str, tname: str):
385 NoReferenceError.__init__(self, message)
386 self.table_name = tname
387
388 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
389 return self.__class__, (self.args[0], self.table_name)
390
391
392class NoReferencedColumnError(NoReferenceError):
393 """Raised by ``ForeignKey`` when the referred ``Column`` cannot be
394 located.
395
396 """
397
398 def __init__(self, message: str, tname: str, cname: str):
399 NoReferenceError.__init__(self, message)
400 self.table_name = tname
401 self.column_name = cname
402
403 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
404 return (
405 self.__class__,
406 (self.args[0], self.table_name, self.column_name),
407 )
408
409
410class NoSuchTableError(InvalidRequestError):
411 """Table does not exist or is not visible to a connection."""
412
413
414class UnreflectableTableError(InvalidRequestError):
415 """Table exists but can't be reflected for some reason.
416
417 .. versionadded:: 1.2
418
419 """
420
421
422class UnboundExecutionError(InvalidRequestError):
423 """SQL was attempted without a database connection to execute it on."""
424
425
426class DontWrapMixin:
427 """A mixin class which, when applied to a user-defined Exception class,
428 will not be wrapped inside of :exc:`.StatementError` if the error is
429 emitted within the process of executing a statement.
430
431 E.g.::
432
433 from sqlalchemy.exc import DontWrapMixin
434
435
436 class MyCustomException(Exception, DontWrapMixin):
437 pass
438
439
440 class MySpecialType(TypeDecorator):
441 impl = String
442
443 def process_bind_param(self, value, dialect):
444 if value == "invalid":
445 raise MyCustomException("invalid!")
446
447 """
448
449
450class StatementError(SQLAlchemyError):
451 """An error occurred during execution of a SQL statement.
452
453 :class:`StatementError` wraps the exception raised
454 during execution, and features :attr:`.statement`
455 and :attr:`.params` attributes which supply context regarding
456 the specifics of the statement which had an issue.
457
458 The wrapped exception object is available in
459 the :attr:`.orig` attribute.
460
461 """
462
463 statement: Optional[str] = None
464 """The string SQL statement being invoked when this exception occurred."""
465
466 params: Optional[_AnyExecuteParams] = None
467 """The parameter list being used when this exception occurred."""
468
469 orig: Optional[BaseException] = None
470 """The original exception that was thrown.
471
472 """
473
474 ismulti: Optional[bool] = None
475 """multi parameter passed to repr_params(). None is meaningful."""
476
477 connection_invalidated: bool = False
478
479 def __init__(
480 self,
481 message: str,
482 statement: Optional[str],
483 params: Optional[_AnyExecuteParams],
484 orig: Optional[BaseException],
485 hide_parameters: bool = False,
486 code: Optional[str] = None,
487 ismulti: Optional[bool] = None,
488 ):
489 SQLAlchemyError.__init__(self, message, code=code)
490 self.statement = statement
491 self.params = params
492 self.orig = orig
493 self.ismulti = ismulti
494 self.hide_parameters = hide_parameters
495 self.detail: List[str] = []
496
497 def add_detail(self, msg: str) -> None:
498 self.detail.append(msg)
499
500 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
501 return (
502 self.__class__,
503 (
504 self.args[0],
505 self.statement,
506 self.params,
507 self.orig,
508 self.hide_parameters,
509 self.__dict__.get("code"),
510 self.ismulti,
511 ),
512 {"detail": self.detail},
513 )
514
515 @_preloaded.preload_module("sqlalchemy.sql.util")
516 def _sql_message(self) -> str:
517 util = _preloaded.sql_util
518
519 details = [self._message()]
520 if self.statement:
521 stmt_detail = "[SQL: %s]" % self.statement
522 details.append(stmt_detail)
523 if self.params:
524 if self.hide_parameters:
525 details.append(
526 "[SQL parameters hidden due to hide_parameters=True]"
527 )
528 else:
529 params_repr = util._repr_params(
530 self.params, 10, ismulti=self.ismulti
531 )
532 details.append("[parameters: %r]" % params_repr)
533 code_str = self._code_str()
534 if code_str:
535 details.append(code_str)
536 return "\n".join(["(%s)" % det for det in self.detail] + details)
537
538
539class DBAPIError(StatementError):
540 """Raised when the execution of a database operation fails.
541
542 Wraps exceptions raised by the DB-API underlying the
543 database operation. Driver-specific implementations of the standard
544 DB-API exception types are wrapped by matching sub-types of SQLAlchemy's
545 :class:`DBAPIError` when possible. DB-API's ``Error`` type maps to
546 :class:`DBAPIError` in SQLAlchemy, otherwise the names are identical. Note
547 that there is no guarantee that different DB-API implementations will
548 raise the same exception type for any given error condition.
549
550 :class:`DBAPIError` features :attr:`~.StatementError.statement`
551 and :attr:`~.StatementError.params` attributes which supply context
552 regarding the specifics of the statement which had an issue, for the
553 typical case when the error was raised within the context of
554 emitting a SQL statement.
555
556 The wrapped exception object is available in the
557 :attr:`~.StatementError.orig` attribute. Its type and properties are
558 DB-API implementation specific.
559
560 """
561
562 code = "dbapi"
563
564 @overload
565 @classmethod
566 def instance(
567 cls,
568 statement: Optional[str],
569 params: Optional[_AnyExecuteParams],
570 orig: Exception,
571 dbapi_base_err: Type[Exception],
572 hide_parameters: bool = False,
573 connection_invalidated: bool = False,
574 dialect: Optional[Dialect] = None,
575 ismulti: Optional[bool] = None,
576 ) -> StatementError: ...
577
578 @overload
579 @classmethod
580 def instance(
581 cls,
582 statement: Optional[str],
583 params: Optional[_AnyExecuteParams],
584 orig: DontWrapMixin,
585 dbapi_base_err: Type[Exception],
586 hide_parameters: bool = False,
587 connection_invalidated: bool = False,
588 dialect: Optional[Dialect] = None,
589 ismulti: Optional[bool] = None,
590 ) -> DontWrapMixin: ...
591
592 @overload
593 @classmethod
594 def instance(
595 cls,
596 statement: Optional[str],
597 params: Optional[_AnyExecuteParams],
598 orig: BaseException,
599 dbapi_base_err: Type[Exception],
600 hide_parameters: bool = False,
601 connection_invalidated: bool = False,
602 dialect: Optional[Dialect] = None,
603 ismulti: Optional[bool] = None,
604 ) -> BaseException: ...
605
606 @classmethod
607 def instance(
608 cls,
609 statement: Optional[str],
610 params: Optional[_AnyExecuteParams],
611 orig: Union[BaseException, DontWrapMixin],
612 dbapi_base_err: Type[Exception],
613 hide_parameters: bool = False,
614 connection_invalidated: bool = False,
615 dialect: Optional[Dialect] = None,
616 ismulti: Optional[bool] = None,
617 ) -> Union[BaseException, DontWrapMixin]:
618 # Don't ever wrap these, just return them directly as if
619 # DBAPIError didn't exist.
620 if (
621 isinstance(orig, BaseException) and not isinstance(orig, Exception)
622 ) or isinstance(orig, DontWrapMixin):
623 return orig
624
625 if orig is not None:
626 # not a DBAPI error, statement is present.
627 # raise a StatementError
628 if isinstance(orig, SQLAlchemyError) and statement:
629 return StatementError(
630 "(%s.%s) %s"
631 % (
632 orig.__class__.__module__,
633 orig.__class__.__name__,
634 orig.args[0],
635 ),
636 statement,
637 params,
638 orig,
639 hide_parameters=hide_parameters,
640 code=orig.code,
641 ismulti=ismulti,
642 )
643 elif not isinstance(orig, dbapi_base_err) and statement:
644 return StatementError(
645 "(%s.%s) %s"
646 % (
647 orig.__class__.__module__,
648 orig.__class__.__name__,
649 orig,
650 ),
651 statement,
652 params,
653 orig,
654 hide_parameters=hide_parameters,
655 ismulti=ismulti,
656 )
657
658 glob = globals()
659 for super_ in orig.__class__.__mro__:
660 name = super_.__name__
661 if dialect:
662 name = dialect.dbapi_exception_translation_map.get(
663 name, name
664 )
665 if name in glob and issubclass(glob[name], DBAPIError):
666 cls = glob[name]
667 break
668
669 return cls(
670 statement,
671 params,
672 orig,
673 connection_invalidated=connection_invalidated,
674 hide_parameters=hide_parameters,
675 code=cls.code,
676 ismulti=ismulti,
677 )
678
679 def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
680 return (
681 self.__class__,
682 (
683 self.statement,
684 self.params,
685 self.orig,
686 self.hide_parameters,
687 self.connection_invalidated,
688 self.__dict__.get("code"),
689 self.ismulti,
690 ),
691 {"detail": self.detail},
692 )
693
694 def __init__(
695 self,
696 statement: Optional[str],
697 params: Optional[_AnyExecuteParams],
698 orig: BaseException,
699 hide_parameters: bool = False,
700 connection_invalidated: bool = False,
701 code: Optional[str] = None,
702 ismulti: Optional[bool] = None,
703 ):
704 try:
705 text = str(orig)
706 except Exception as e:
707 text = "Error in str() of DB-API-generated exception: " + str(e)
708 StatementError.__init__(
709 self,
710 "(%s.%s) %s"
711 % (orig.__class__.__module__, orig.__class__.__name__, text),
712 statement,
713 params,
714 orig,
715 hide_parameters,
716 code=code,
717 ismulti=ismulti,
718 )
719 self.connection_invalidated = connection_invalidated
720
721
722class InterfaceError(DBAPIError):
723 """Wraps a DB-API InterfaceError."""
724
725 code = "rvf5"
726
727
728class DatabaseError(DBAPIError):
729 """Wraps a DB-API DatabaseError."""
730
731 code = "4xp6"
732
733
734class DataError(DatabaseError):
735 """Wraps a DB-API DataError."""
736
737 code = "9h9h"
738
739
740class OperationalError(DatabaseError):
741 """Wraps a DB-API OperationalError."""
742
743 code = "e3q8"
744
745
746class IntegrityError(DatabaseError):
747 """Wraps a DB-API IntegrityError."""
748
749 code = "gkpj"
750
751
752class InternalError(DatabaseError):
753 """Wraps a DB-API InternalError."""
754
755 code = "2j85"
756
757
758class ProgrammingError(DatabaseError):
759 """Wraps a DB-API ProgrammingError."""
760
761 code = "f405"
762
763
764class NotSupportedError(DatabaseError):
765 """Wraps a DB-API NotSupportedError."""
766
767 code = "tw8g"
768
769
770# Warnings
771
772
773class SATestSuiteWarning(Warning):
774 """warning for a condition detected during tests that is non-fatal
775
776 Currently outside of SAWarning so that we can work around tools like
777 Alembic doing the wrong thing with warnings.
778
779 """
780
781
782class SADeprecationWarning(HasDescriptionCode, DeprecationWarning):
783 """Issued for usage of deprecated APIs."""
784
785 deprecated_since: Optional[str] = None
786 "Indicates the version that started raising this deprecation warning"
787
788
789class Base20DeprecationWarning(SADeprecationWarning):
790 """Issued for usage of APIs specifically deprecated or legacy in
791 SQLAlchemy 2.0.
792
793 .. seealso::
794
795 :ref:`error_b8d9`.
796
797 :ref:`deprecation_20_mode`
798
799 """
800
801 deprecated_since: Optional[str] = "1.4"
802 "Indicates the version that started raising this deprecation warning"
803
804 def __str__(self) -> str:
805 return (
806 super().__str__()
807 + " (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)"
808 )
809
810
811class LegacyAPIWarning(Base20DeprecationWarning):
812 """indicates an API that is in 'legacy' status, a long term deprecation."""
813
814
815class MovedIn20Warning(Base20DeprecationWarning):
816 """Subtype of RemovedIn20Warning to indicate an API that moved only."""
817
818
819class SAPendingDeprecationWarning(PendingDeprecationWarning):
820 """A similar warning as :class:`_exc.SADeprecationWarning`, this warning
821 is not used in modern versions of SQLAlchemy.
822
823 """
824
825 deprecated_since: Optional[str] = None
826 "Indicates the version that started raising this deprecation warning"
827
828
829class SAWarning(HasDescriptionCode, RuntimeWarning):
830 """Issued at runtime."""
831
832 _what_are_we = "warning"
833 