Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
exc.py833 linesDownload Raw Back to sqlalchemy
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 
codekingpro/portable-devtools · Team Ai