Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/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-2024 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 UnmappedError(sa_exc.InvalidRequestError):
69 """Base for exceptions that involve expected mappings not present."""
72class ObjectDereferencedError(sa_exc.SQLAlchemyError):
73 """An operation cannot complete due to an object being garbage
74 collected.
76 """
79class DetachedInstanceError(sa_exc.SQLAlchemyError):
80 """An attempt to access unloaded attributes on a
81 mapped instance that is detached."""
83 code = "bhk3"
86class UnmappedInstanceError(UnmappedError):
87 """An mapping operation was requested for an unknown instance."""
89 @util.preload_module("sqlalchemy.orm.base")
90 def __init__(self, obj: object, msg: Optional[str] = None):
91 base = util.preloaded.orm_base
93 if not msg:
94 try:
95 base.class_mapper(type(obj))
96 name = _safe_cls_name(type(obj))
97 msg = (
98 "Class %r is mapped, but this instance lacks "
99 "instrumentation. This occurs when the instance "
100 "is created before sqlalchemy.orm.mapper(%s) "
101 "was called." % (name, name)
102 )
103 except UnmappedClassError:
104 msg = f"Class '{_safe_cls_name(type(obj))}' is not mapped"
105 if isinstance(obj, type):
106 msg += (
107 "; was a class (%s) supplied where an instance was "
108 "required?" % _safe_cls_name(obj)
109 )
110 UnmappedError.__init__(self, msg)
112 def __reduce__(self) -> Any:
113 return self.__class__, (None, self.args[0])
116class UnmappedClassError(UnmappedError):
117 """An mapping operation was requested for an unknown class."""
119 def __init__(self, cls: Type[_T], msg: Optional[str] = None):
120 if not msg:
121 msg = _default_unmapped(cls)
122 UnmappedError.__init__(self, msg)
124 def __reduce__(self) -> Any:
125 return self.__class__, (None, self.args[0])
128class ObjectDeletedError(sa_exc.InvalidRequestError):
129 """A refresh operation failed to retrieve the database
130 row corresponding to an object's known primary key identity.
132 A refresh operation proceeds when an expired attribute is
133 accessed on an object, or when :meth:`_query.Query.get` is
134 used to retrieve an object which is, upon retrieval, detected
135 as expired. A SELECT is emitted for the target row
136 based on primary key; if no row is returned, this
137 exception is raised.
139 The true meaning of this exception is simply that
140 no row exists for the primary key identifier associated
141 with a persistent object. The row may have been
142 deleted, or in some cases the primary key updated
143 to a new value, outside of the ORM's management of the target
144 object.
146 """
148 @util.preload_module("sqlalchemy.orm.base")
149 def __init__(self, state: InstanceState[Any], msg: Optional[str] = None):
150 base = util.preloaded.orm_base
152 if not msg:
153 msg = (
154 "Instance '%s' has been deleted, or its "
155 "row is otherwise not present." % base.state_str(state)
156 )
158 sa_exc.InvalidRequestError.__init__(self, msg)
160 def __reduce__(self) -> Any:
161 return self.__class__, (None, self.args[0])
164class UnmappedColumnError(sa_exc.InvalidRequestError):
165 """Mapping operation was requested on an unknown column."""
168class LoaderStrategyException(sa_exc.InvalidRequestError):
169 """A loader strategy for an attribute does not exist."""
171 def __init__(
172 self,
173 applied_to_property_type: Type[Any],
174 requesting_property: MapperProperty[Any],
175 applies_to: Optional[Type[MapperProperty[Any]]],
176 actual_strategy_type: Optional[Type[LoaderStrategy]],
177 strategy_key: Tuple[Any, ...],
178 ):
179 if actual_strategy_type is None:
180 sa_exc.InvalidRequestError.__init__(
181 self,
182 "Can't find strategy %s for %s"
183 % (strategy_key, requesting_property),
184 )
185 else:
186 assert applies_to is not None
187 sa_exc.InvalidRequestError.__init__(
188 self,
189 'Can\'t apply "%s" strategy to property "%s", '
190 'which is a "%s"; this loader strategy is intended '
191 'to be used with a "%s".'
192 % (
193 util.clsname_as_plain_name(actual_strategy_type),
194 requesting_property,
195 _mapper_property_as_plain_name(applied_to_property_type),
196 _mapper_property_as_plain_name(applies_to),
197 ),
198 )
201def _safe_cls_name(cls: Type[Any]) -> str:
202 cls_name: Optional[str]
203 try:
204 cls_name = ".".join((cls.__module__, cls.__name__))
205 except AttributeError:
206 cls_name = getattr(cls, "__name__", None)
207 if cls_name is None:
208 cls_name = repr(cls)
209 return cls_name
212@util.preload_module("sqlalchemy.orm.base")
213def _default_unmapped(cls: Type[Any]) -> Optional[str]:
214 base = util.preloaded.orm_base
216 try:
217 mappers = base.manager_of_class(cls).mappers # type: ignore
218 except (
219 UnmappedClassError,
220 TypeError,
221 ) + NO_STATE:
222 mappers = {}
223 name = _safe_cls_name(cls)
225 if not mappers:
226 return f"Class '{name}' is not mapped"
227 else:
228 return None