Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/exc.py: 48%
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# orm/exc.py
2# Copyright (C) 2005-2025 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"""SQLAlchemy ORM exceptions."""
10from __future__ import annotations
12from typing import Any
13from typing import Optional
14from typing import Tuple
15from typing import Type
16from typing import TYPE_CHECKING
17from typing import TypeVar
19from .util import _mapper_property_as_plain_name
20from .. import exc as sa_exc
21from .. import util
22from ..exc import MultipleResultsFound # noqa
23from ..exc import NoResultFound # noqa
25if TYPE_CHECKING:
26 from .interfaces import LoaderStrategy
27 from .interfaces import MapperProperty
28 from .state import InstanceState
30_T = TypeVar("_T", bound=Any)
32NO_STATE = (AttributeError, KeyError)
33"""Exception types that may be raised by instrumentation implementations."""
36class StaleDataError(sa_exc.SQLAlchemyError):
37 """An operation encountered database state that is unaccounted for.
39 Conditions which cause this to happen include:
41 * A flush may have attempted to update or delete rows
42 and an unexpected number of rows were matched during
43 the UPDATE or DELETE statement. Note that when
44 version_id_col is used, rows in UPDATE or DELETE statements
45 are also matched against the current known version
46 identifier.
48 * A mapped object with version_id_col was refreshed,
49 and the version number coming back from the database does
50 not match that of the object itself.
52 * A object is detached from its parent object, however
53 the object was previously attached to a different parent
54 identity which was garbage collected, and a decision
55 cannot be made if the new parent was really the most
56 recent "parent".
58 """
61ConcurrentModificationError = StaleDataError
64class FlushError(sa_exc.SQLAlchemyError):
65 """A invalid condition was detected during flush()."""
68class MappedAnnotationError(sa_exc.ArgumentError):
69 """Raised when ORM annotated declarative cannot interpret the
70 expression present inside of the :class:`.Mapped` construct.
72 .. versionadded:: 2.0.40
74 """
77class UnmappedError(sa_exc.InvalidRequestError):
78 """Base for exceptions that involve expected mappings not present."""
81class ObjectDereferencedError(sa_exc.SQLAlchemyError):
82 """An operation cannot complete due to an object being garbage
83 collected.
85 """
88class DetachedInstanceError(sa_exc.SQLAlchemyError):
89 """An attempt to access unloaded attributes on a
90 mapped instance that is detached."""
92 code = "bhk3"
95class UnmappedInstanceError(UnmappedError):
96 """An mapping operation was requested for an unknown instance."""
98 @util.preload_module("sqlalchemy.orm.base")
99 def __init__(self, obj: object, msg: Optional[str] = None):
100 base = util.preloaded.orm_base
102 if not msg:
103 try:
104 base.class_mapper(type(obj))
105 name = _safe_cls_name(type(obj))
106 msg = (
107 "Class %r is mapped, but this instance lacks "
108 "instrumentation. This occurs when the instance "
109 "is created before sqlalchemy.orm.mapper(%s) "
110 "was called." % (name, name)
111 )
112 except UnmappedClassError:
113 msg = f"Class '{_safe_cls_name(type(obj))}' is not mapped"
114 if isinstance(obj, type):
115 msg += (
116 "; was a class (%s) supplied where an instance was "
117 "required?" % _safe_cls_name(obj)
118 )
119 UnmappedError.__init__(self, msg)
121 def __reduce__(self) -> Any:
122 return self.__class__, (None, self.args[0])
125class UnmappedClassError(UnmappedError):
126 """An mapping operation was requested for an unknown class."""
128 def __init__(self, cls: Type[_T], msg: Optional[str] = None):
129 if not msg:
130 msg = _default_unmapped(cls)
131 UnmappedError.__init__(self, msg)
133 def __reduce__(self) -> Any:
134 return self.__class__, (None, self.args[0])
137class ObjectDeletedError(sa_exc.InvalidRequestError):
138 """A refresh operation failed to retrieve the database
139 row corresponding to an object's known primary key identity.
141 A refresh operation proceeds when an expired attribute is
142 accessed on an object, or when :meth:`_query.Query.get` is
143 used to retrieve an object which is, upon retrieval, detected
144 as expired. A SELECT is emitted for the target row
145 based on primary key; if no row is returned, this
146 exception is raised.
148 The true meaning of this exception is simply that
149 no row exists for the primary key identifier associated
150 with a persistent object. The row may have been
151 deleted, or in some cases the primary key updated
152 to a new value, outside of the ORM's management of the target
153 object.
155 """
157 @util.preload_module("sqlalchemy.orm.base")
158 def __init__(self, state: InstanceState[Any], msg: Optional[str] = None):
159 base = util.preloaded.orm_base
161 if not msg:
162 msg = (
163 "Instance '%s' has been deleted, or its "
164 "row is otherwise not present." % base.state_str(state)
165 )
167 sa_exc.InvalidRequestError.__init__(self, msg)
169 def __reduce__(self) -> Any:
170 return self.__class__, (None, self.args[0])
173class UnmappedColumnError(sa_exc.InvalidRequestError):
174 """Mapping operation was requested on an unknown column."""
177class LoaderStrategyException(sa_exc.InvalidRequestError):
178 """A loader strategy for an attribute does not exist."""
180 def __init__(
181 self,
182 applied_to_property_type: Type[Any],
183 requesting_property: MapperProperty[Any],
184 applies_to: Optional[Type[MapperProperty[Any]]],
185 actual_strategy_type: Optional[Type[LoaderStrategy]],
186 strategy_key: Tuple[Any, ...],
187 ):
188 if actual_strategy_type is None:
189 sa_exc.InvalidRequestError.__init__(
190 self,
191 "Can't find strategy %s for %s"
192 % (strategy_key, requesting_property),
193 )
194 else:
195 assert applies_to is not None
196 sa_exc.InvalidRequestError.__init__(
197 self,
198 'Can\'t apply "%s" strategy to property "%s", '
199 'which is a "%s"; this loader strategy is intended '
200 'to be used with a "%s".'
201 % (
202 util.clsname_as_plain_name(actual_strategy_type),
203 requesting_property,
204 _mapper_property_as_plain_name(applied_to_property_type),
205 _mapper_property_as_plain_name(applies_to),
206 ),
207 )
210def _safe_cls_name(cls: Type[Any]) -> str:
211 cls_name: Optional[str]
212 try:
213 cls_name = ".".join((cls.__module__, cls.__name__))
214 except AttributeError:
215 cls_name = getattr(cls, "__name__", None)
216 if cls_name is None:
217 cls_name = repr(cls)
218 return cls_name
221@util.preload_module("sqlalchemy.orm.base")
222def _default_unmapped(cls: Type[Any]) -> Optional[str]:
223 base = util.preloaded.orm_base
225 try:
226 mappers = base.manager_of_class(cls).mappers # type: ignore
227 except (
228 UnmappedClassError,
229 TypeError,
230 ) + NO_STATE:
231 mappers = {}
232 name = _safe_cls_name(cls)
234 if not mappers:
235 return f"Class '{name}' is not mapped"
236 else:
237 return None