SQLAlchemy 2.0.47__cp313-cp313t-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlalchemy/__init__.py +283 -0
- sqlalchemy/connectors/__init__.py +18 -0
- sqlalchemy/connectors/aioodbc.py +184 -0
- sqlalchemy/connectors/asyncio.py +429 -0
- sqlalchemy/connectors/pyodbc.py +250 -0
- sqlalchemy/cyextension/__init__.py +6 -0
- sqlalchemy/cyextension/collections.cp313t-win32.pyd +0 -0
- sqlalchemy/cyextension/collections.pyx +409 -0
- sqlalchemy/cyextension/immutabledict.cp313t-win32.pyd +0 -0
- sqlalchemy/cyextension/immutabledict.pxd +8 -0
- sqlalchemy/cyextension/immutabledict.pyx +133 -0
- sqlalchemy/cyextension/processors.cp313t-win32.pyd +0 -0
- sqlalchemy/cyextension/processors.pyx +68 -0
- sqlalchemy/cyextension/resultproxy.cp313t-win32.pyd +0 -0
- sqlalchemy/cyextension/resultproxy.pyx +102 -0
- sqlalchemy/cyextension/util.cp313t-win32.pyd +0 -0
- sqlalchemy/cyextension/util.pyx +90 -0
- sqlalchemy/dialects/__init__.py +62 -0
- sqlalchemy/dialects/_typing.py +30 -0
- sqlalchemy/dialects/mssql/__init__.py +88 -0
- sqlalchemy/dialects/mssql/aioodbc.py +63 -0
- sqlalchemy/dialects/mssql/base.py +4093 -0
- sqlalchemy/dialects/mssql/information_schema.py +285 -0
- sqlalchemy/dialects/mssql/json.py +129 -0
- sqlalchemy/dialects/mssql/provision.py +185 -0
- sqlalchemy/dialects/mssql/pymssql.py +126 -0
- sqlalchemy/dialects/mssql/pyodbc.py +760 -0
- sqlalchemy/dialects/mysql/__init__.py +104 -0
- sqlalchemy/dialects/mysql/aiomysql.py +250 -0
- sqlalchemy/dialects/mysql/asyncmy.py +231 -0
- sqlalchemy/dialects/mysql/base.py +3949 -0
- sqlalchemy/dialects/mysql/cymysql.py +106 -0
- sqlalchemy/dialects/mysql/dml.py +225 -0
- sqlalchemy/dialects/mysql/enumerated.py +282 -0
- sqlalchemy/dialects/mysql/expression.py +146 -0
- sqlalchemy/dialects/mysql/json.py +91 -0
- sqlalchemy/dialects/mysql/mariadb.py +72 -0
- sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
- sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
- sqlalchemy/dialects/mysql/mysqldb.py +314 -0
- sqlalchemy/dialects/mysql/provision.py +153 -0
- sqlalchemy/dialects/mysql/pymysql.py +158 -0
- sqlalchemy/dialects/mysql/pyodbc.py +157 -0
- sqlalchemy/dialects/mysql/reflection.py +727 -0
- sqlalchemy/dialects/mysql/reserved_words.py +570 -0
- sqlalchemy/dialects/mysql/types.py +835 -0
- sqlalchemy/dialects/oracle/__init__.py +81 -0
- sqlalchemy/dialects/oracle/base.py +3802 -0
- sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
- sqlalchemy/dialects/oracle/dictionary.py +507 -0
- sqlalchemy/dialects/oracle/oracledb.py +941 -0
- sqlalchemy/dialects/oracle/provision.py +297 -0
- sqlalchemy/dialects/oracle/types.py +316 -0
- sqlalchemy/dialects/oracle/vector.py +365 -0
- sqlalchemy/dialects/postgresql/__init__.py +167 -0
- sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
- sqlalchemy/dialects/postgresql/array.py +519 -0
- sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
- sqlalchemy/dialects/postgresql/base.py +5378 -0
- sqlalchemy/dialects/postgresql/dml.py +339 -0
- sqlalchemy/dialects/postgresql/ext.py +540 -0
- sqlalchemy/dialects/postgresql/hstore.py +406 -0
- sqlalchemy/dialects/postgresql/json.py +404 -0
- sqlalchemy/dialects/postgresql/named_types.py +524 -0
- sqlalchemy/dialects/postgresql/operators.py +129 -0
- sqlalchemy/dialects/postgresql/pg8000.py +669 -0
- sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
- sqlalchemy/dialects/postgresql/provision.py +183 -0
- sqlalchemy/dialects/postgresql/psycopg.py +862 -0
- sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
- sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
- sqlalchemy/dialects/postgresql/ranges.py +1031 -0
- sqlalchemy/dialects/postgresql/types.py +313 -0
- sqlalchemy/dialects/sqlite/__init__.py +57 -0
- sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
- sqlalchemy/dialects/sqlite/base.py +3056 -0
- sqlalchemy/dialects/sqlite/dml.py +263 -0
- sqlalchemy/dialects/sqlite/json.py +92 -0
- sqlalchemy/dialects/sqlite/provision.py +229 -0
- sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
- sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
- sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
- sqlalchemy/engine/__init__.py +62 -0
- sqlalchemy/engine/_py_processors.py +136 -0
- sqlalchemy/engine/_py_row.py +128 -0
- sqlalchemy/engine/_py_util.py +74 -0
- sqlalchemy/engine/base.py +3390 -0
- sqlalchemy/engine/characteristics.py +155 -0
- sqlalchemy/engine/create.py +893 -0
- sqlalchemy/engine/cursor.py +2298 -0
- sqlalchemy/engine/default.py +2394 -0
- sqlalchemy/engine/events.py +965 -0
- sqlalchemy/engine/interfaces.py +3471 -0
- sqlalchemy/engine/mock.py +134 -0
- sqlalchemy/engine/processors.py +61 -0
- sqlalchemy/engine/reflection.py +2102 -0
- sqlalchemy/engine/result.py +2399 -0
- sqlalchemy/engine/row.py +400 -0
- sqlalchemy/engine/strategies.py +16 -0
- sqlalchemy/engine/url.py +924 -0
- sqlalchemy/engine/util.py +167 -0
- sqlalchemy/event/__init__.py +26 -0
- sqlalchemy/event/api.py +220 -0
- sqlalchemy/event/attr.py +676 -0
- sqlalchemy/event/base.py +472 -0
- sqlalchemy/event/legacy.py +258 -0
- sqlalchemy/event/registry.py +390 -0
- sqlalchemy/events.py +17 -0
- sqlalchemy/exc.py +832 -0
- sqlalchemy/ext/__init__.py +11 -0
- sqlalchemy/ext/associationproxy.py +2027 -0
- sqlalchemy/ext/asyncio/__init__.py +25 -0
- sqlalchemy/ext/asyncio/base.py +281 -0
- sqlalchemy/ext/asyncio/engine.py +1471 -0
- sqlalchemy/ext/asyncio/exc.py +21 -0
- sqlalchemy/ext/asyncio/result.py +965 -0
- sqlalchemy/ext/asyncio/scoping.py +1599 -0
- sqlalchemy/ext/asyncio/session.py +1947 -0
- sqlalchemy/ext/automap.py +1701 -0
- sqlalchemy/ext/baked.py +570 -0
- sqlalchemy/ext/compiler.py +600 -0
- sqlalchemy/ext/declarative/__init__.py +65 -0
- sqlalchemy/ext/declarative/extensions.py +564 -0
- sqlalchemy/ext/horizontal_shard.py +478 -0
- sqlalchemy/ext/hybrid.py +1535 -0
- sqlalchemy/ext/indexable.py +364 -0
- sqlalchemy/ext/instrumentation.py +450 -0
- sqlalchemy/ext/mutable.py +1085 -0
- sqlalchemy/ext/mypy/__init__.py +6 -0
- sqlalchemy/ext/mypy/apply.py +324 -0
- sqlalchemy/ext/mypy/decl_class.py +515 -0
- sqlalchemy/ext/mypy/infer.py +590 -0
- sqlalchemy/ext/mypy/names.py +335 -0
- sqlalchemy/ext/mypy/plugin.py +303 -0
- sqlalchemy/ext/mypy/util.py +357 -0
- sqlalchemy/ext/orderinglist.py +439 -0
- sqlalchemy/ext/serializer.py +185 -0
- sqlalchemy/future/__init__.py +16 -0
- sqlalchemy/future/engine.py +15 -0
- sqlalchemy/inspection.py +174 -0
- sqlalchemy/log.py +288 -0
- sqlalchemy/orm/__init__.py +171 -0
- sqlalchemy/orm/_orm_constructors.py +2661 -0
- sqlalchemy/orm/_typing.py +179 -0
- sqlalchemy/orm/attributes.py +2845 -0
- sqlalchemy/orm/base.py +971 -0
- sqlalchemy/orm/bulk_persistence.py +2135 -0
- sqlalchemy/orm/clsregistry.py +571 -0
- sqlalchemy/orm/collections.py +1627 -0
- sqlalchemy/orm/context.py +3334 -0
- sqlalchemy/orm/decl_api.py +2004 -0
- sqlalchemy/orm/decl_base.py +2192 -0
- sqlalchemy/orm/dependency.py +1302 -0
- sqlalchemy/orm/descriptor_props.py +1092 -0
- sqlalchemy/orm/dynamic.py +300 -0
- sqlalchemy/orm/evaluator.py +379 -0
- sqlalchemy/orm/events.py +3252 -0
- sqlalchemy/orm/exc.py +237 -0
- sqlalchemy/orm/identity.py +302 -0
- sqlalchemy/orm/instrumentation.py +754 -0
- sqlalchemy/orm/interfaces.py +1496 -0
- sqlalchemy/orm/loading.py +1686 -0
- sqlalchemy/orm/mapped_collection.py +557 -0
- sqlalchemy/orm/mapper.py +4444 -0
- sqlalchemy/orm/path_registry.py +809 -0
- sqlalchemy/orm/persistence.py +1788 -0
- sqlalchemy/orm/properties.py +935 -0
- sqlalchemy/orm/query.py +3459 -0
- sqlalchemy/orm/relationships.py +3508 -0
- sqlalchemy/orm/scoping.py +2148 -0
- sqlalchemy/orm/session.py +5280 -0
- sqlalchemy/orm/state.py +1168 -0
- sqlalchemy/orm/state_changes.py +196 -0
- sqlalchemy/orm/strategies.py +3470 -0
- sqlalchemy/orm/strategy_options.py +2568 -0
- sqlalchemy/orm/sync.py +164 -0
- sqlalchemy/orm/unitofwork.py +796 -0
- sqlalchemy/orm/util.py +2403 -0
- sqlalchemy/orm/writeonly.py +674 -0
- sqlalchemy/pool/__init__.py +44 -0
- sqlalchemy/pool/base.py +1524 -0
- sqlalchemy/pool/events.py +375 -0
- sqlalchemy/pool/impl.py +588 -0
- sqlalchemy/py.typed +0 -0
- sqlalchemy/schema.py +69 -0
- sqlalchemy/sql/__init__.py +145 -0
- sqlalchemy/sql/_dml_constructors.py +132 -0
- sqlalchemy/sql/_elements_constructors.py +1872 -0
- sqlalchemy/sql/_orm_types.py +20 -0
- sqlalchemy/sql/_py_util.py +75 -0
- sqlalchemy/sql/_selectable_constructors.py +763 -0
- sqlalchemy/sql/_typing.py +482 -0
- sqlalchemy/sql/annotation.py +587 -0
- sqlalchemy/sql/base.py +2293 -0
- sqlalchemy/sql/cache_key.py +1057 -0
- sqlalchemy/sql/coercions.py +1404 -0
- sqlalchemy/sql/compiler.py +8081 -0
- sqlalchemy/sql/crud.py +1752 -0
- sqlalchemy/sql/ddl.py +1444 -0
- sqlalchemy/sql/default_comparator.py +551 -0
- sqlalchemy/sql/dml.py +1850 -0
- sqlalchemy/sql/elements.py +5589 -0
- sqlalchemy/sql/events.py +458 -0
- sqlalchemy/sql/expression.py +159 -0
- sqlalchemy/sql/functions.py +2158 -0
- sqlalchemy/sql/lambdas.py +1442 -0
- sqlalchemy/sql/naming.py +209 -0
- sqlalchemy/sql/operators.py +2623 -0
- sqlalchemy/sql/roles.py +323 -0
- sqlalchemy/sql/schema.py +6222 -0
- sqlalchemy/sql/selectable.py +7265 -0
- sqlalchemy/sql/sqltypes.py +3930 -0
- sqlalchemy/sql/traversals.py +1024 -0
- sqlalchemy/sql/type_api.py +2368 -0
- sqlalchemy/sql/util.py +1485 -0
- sqlalchemy/sql/visitors.py +1164 -0
- sqlalchemy/testing/__init__.py +96 -0
- sqlalchemy/testing/assertions.py +994 -0
- sqlalchemy/testing/assertsql.py +520 -0
- sqlalchemy/testing/asyncio.py +135 -0
- sqlalchemy/testing/config.py +434 -0
- sqlalchemy/testing/engines.py +483 -0
- sqlalchemy/testing/entities.py +117 -0
- sqlalchemy/testing/exclusions.py +476 -0
- sqlalchemy/testing/fixtures/__init__.py +28 -0
- sqlalchemy/testing/fixtures/base.py +384 -0
- sqlalchemy/testing/fixtures/mypy.py +332 -0
- sqlalchemy/testing/fixtures/orm.py +227 -0
- sqlalchemy/testing/fixtures/sql.py +482 -0
- sqlalchemy/testing/pickleable.py +155 -0
- sqlalchemy/testing/plugin/__init__.py +6 -0
- sqlalchemy/testing/plugin/bootstrap.py +51 -0
- sqlalchemy/testing/plugin/plugin_base.py +828 -0
- sqlalchemy/testing/plugin/pytestplugin.py +892 -0
- sqlalchemy/testing/profiling.py +329 -0
- sqlalchemy/testing/provision.py +603 -0
- sqlalchemy/testing/requirements.py +1945 -0
- sqlalchemy/testing/schema.py +198 -0
- sqlalchemy/testing/suite/__init__.py +19 -0
- sqlalchemy/testing/suite/test_cte.py +237 -0
- sqlalchemy/testing/suite/test_ddl.py +389 -0
- sqlalchemy/testing/suite/test_deprecations.py +153 -0
- sqlalchemy/testing/suite/test_dialect.py +776 -0
- sqlalchemy/testing/suite/test_insert.py +630 -0
- sqlalchemy/testing/suite/test_reflection.py +3557 -0
- sqlalchemy/testing/suite/test_results.py +504 -0
- sqlalchemy/testing/suite/test_rowcount.py +258 -0
- sqlalchemy/testing/suite/test_select.py +2010 -0
- sqlalchemy/testing/suite/test_sequence.py +317 -0
- sqlalchemy/testing/suite/test_types.py +2147 -0
- sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
- sqlalchemy/testing/suite/test_update_delete.py +139 -0
- sqlalchemy/testing/util.py +535 -0
- sqlalchemy/testing/warnings.py +52 -0
- sqlalchemy/types.py +74 -0
- sqlalchemy/util/__init__.py +162 -0
- sqlalchemy/util/_collections.py +712 -0
- sqlalchemy/util/_concurrency_py3k.py +288 -0
- sqlalchemy/util/_has_cy.py +40 -0
- sqlalchemy/util/_py_collections.py +541 -0
- sqlalchemy/util/compat.py +421 -0
- sqlalchemy/util/concurrency.py +110 -0
- sqlalchemy/util/deprecations.py +401 -0
- sqlalchemy/util/langhelpers.py +2203 -0
- sqlalchemy/util/preloaded.py +150 -0
- sqlalchemy/util/queue.py +322 -0
- sqlalchemy/util/tool_support.py +201 -0
- sqlalchemy/util/topological.py +120 -0
- sqlalchemy/util/typing.py +734 -0
- sqlalchemy-2.0.47.dist-info/METADATA +243 -0
- sqlalchemy-2.0.47.dist-info/RECORD +274 -0
- sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
- sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
- sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
sqlalchemy/orm/exc.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# orm/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
|
+
"""SQLAlchemy ORM exceptions."""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
from typing import Type
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
from typing import TypeVar
|
|
18
|
+
|
|
19
|
+
from .util import _mapper_property_as_plain_name
|
|
20
|
+
from .. import exc as sa_exc
|
|
21
|
+
from .. import util
|
|
22
|
+
from ..exc import MultipleResultsFound # noqa
|
|
23
|
+
from ..exc import NoResultFound # noqa
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from .interfaces import LoaderStrategy
|
|
27
|
+
from .interfaces import MapperProperty
|
|
28
|
+
from .state import InstanceState
|
|
29
|
+
|
|
30
|
+
_T = TypeVar("_T", bound=Any)
|
|
31
|
+
|
|
32
|
+
NO_STATE = (AttributeError, KeyError)
|
|
33
|
+
"""Exception types that may be raised by instrumentation implementations."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class StaleDataError(sa_exc.SQLAlchemyError):
|
|
37
|
+
"""An operation encountered database state that is unaccounted for.
|
|
38
|
+
|
|
39
|
+
Conditions which cause this to happen include:
|
|
40
|
+
|
|
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.
|
|
47
|
+
|
|
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.
|
|
51
|
+
|
|
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".
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
ConcurrentModificationError = StaleDataError
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class FlushError(sa_exc.SQLAlchemyError):
|
|
65
|
+
"""A invalid condition was detected during flush()."""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MappedAnnotationError(sa_exc.ArgumentError):
|
|
69
|
+
"""Raised when ORM annotated declarative cannot interpret the
|
|
70
|
+
expression present inside of the :class:`.Mapped` construct.
|
|
71
|
+
|
|
72
|
+
.. versionadded:: 2.0.40
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class UnmappedError(sa_exc.InvalidRequestError):
|
|
78
|
+
"""Base for exceptions that involve expected mappings not present."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ObjectDereferencedError(sa_exc.SQLAlchemyError):
|
|
82
|
+
"""An operation cannot complete due to an object being garbage
|
|
83
|
+
collected.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class DetachedInstanceError(sa_exc.SQLAlchemyError):
|
|
89
|
+
"""An attempt to access unloaded attributes on a
|
|
90
|
+
mapped instance that is detached."""
|
|
91
|
+
|
|
92
|
+
code = "bhk3"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class UnmappedInstanceError(UnmappedError):
|
|
96
|
+
"""An mapping operation was requested for an unknown instance."""
|
|
97
|
+
|
|
98
|
+
@util.preload_module("sqlalchemy.orm.base")
|
|
99
|
+
def __init__(self, obj: object, msg: Optional[str] = None):
|
|
100
|
+
base = util.preloaded.orm_base
|
|
101
|
+
|
|
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)
|
|
120
|
+
|
|
121
|
+
def __reduce__(self) -> Any:
|
|
122
|
+
return self.__class__, (None, self.args[0])
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class UnmappedClassError(UnmappedError):
|
|
126
|
+
"""An mapping operation was requested for an unknown class."""
|
|
127
|
+
|
|
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)
|
|
132
|
+
|
|
133
|
+
def __reduce__(self) -> Any:
|
|
134
|
+
return self.__class__, (None, self.args[0])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ObjectDeletedError(sa_exc.InvalidRequestError):
|
|
138
|
+
"""A refresh operation failed to retrieve the database
|
|
139
|
+
row corresponding to an object's known primary key identity.
|
|
140
|
+
|
|
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.
|
|
147
|
+
|
|
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.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
@util.preload_module("sqlalchemy.orm.base")
|
|
158
|
+
def __init__(self, state: InstanceState[Any], msg: Optional[str] = None):
|
|
159
|
+
base = util.preloaded.orm_base
|
|
160
|
+
|
|
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
|
+
)
|
|
166
|
+
|
|
167
|
+
sa_exc.InvalidRequestError.__init__(self, msg)
|
|
168
|
+
|
|
169
|
+
def __reduce__(self) -> Any:
|
|
170
|
+
return self.__class__, (None, self.args[0])
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class UnmappedColumnError(sa_exc.InvalidRequestError):
|
|
174
|
+
"""Mapping operation was requested on an unknown column."""
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class LoaderStrategyException(sa_exc.InvalidRequestError):
|
|
178
|
+
"""A loader strategy for an attribute does not exist."""
|
|
179
|
+
|
|
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
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _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
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@util.preload_module("sqlalchemy.orm.base")
|
|
222
|
+
def _default_unmapped(cls: Type[Any]) -> Optional[str]:
|
|
223
|
+
base = util.preloaded.orm_base
|
|
224
|
+
|
|
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)
|
|
233
|
+
|
|
234
|
+
if not mappers:
|
|
235
|
+
return f"Class '{name}' is not mapped"
|
|
236
|
+
else:
|
|
237
|
+
return None
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# orm/identity.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
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
from typing import cast
|
|
12
|
+
from typing import Dict
|
|
13
|
+
from typing import Iterable
|
|
14
|
+
from typing import Iterator
|
|
15
|
+
from typing import List
|
|
16
|
+
from typing import NoReturn
|
|
17
|
+
from typing import Optional
|
|
18
|
+
from typing import Set
|
|
19
|
+
from typing import Tuple
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
from typing import TypeVar
|
|
22
|
+
import weakref
|
|
23
|
+
|
|
24
|
+
from . import util as orm_util
|
|
25
|
+
from .. import exc as sa_exc
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from ._typing import _IdentityKeyType
|
|
29
|
+
from .state import InstanceState
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_T = TypeVar("_T", bound=Any)
|
|
33
|
+
|
|
34
|
+
_O = TypeVar("_O", bound=object)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class IdentityMap:
|
|
38
|
+
_wr: weakref.ref[IdentityMap]
|
|
39
|
+
|
|
40
|
+
_dict: Dict[_IdentityKeyType[Any], Any]
|
|
41
|
+
_modified: Set[InstanceState[Any]]
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
self._dict = {}
|
|
45
|
+
self._modified = set()
|
|
46
|
+
self._wr = weakref.ref(self)
|
|
47
|
+
|
|
48
|
+
def _kill(self) -> None:
|
|
49
|
+
self._add_unpresent = _killed # type: ignore
|
|
50
|
+
|
|
51
|
+
def all_states(self) -> List[InstanceState[Any]]:
|
|
52
|
+
raise NotImplementedError()
|
|
53
|
+
|
|
54
|
+
def contains_state(self, state: InstanceState[Any]) -> bool:
|
|
55
|
+
raise NotImplementedError()
|
|
56
|
+
|
|
57
|
+
def __contains__(self, key: _IdentityKeyType[Any]) -> bool:
|
|
58
|
+
raise NotImplementedError()
|
|
59
|
+
|
|
60
|
+
def safe_discard(self, state: InstanceState[Any]) -> None:
|
|
61
|
+
raise NotImplementedError()
|
|
62
|
+
|
|
63
|
+
def __getitem__(self, key: _IdentityKeyType[_O]) -> _O:
|
|
64
|
+
raise NotImplementedError()
|
|
65
|
+
|
|
66
|
+
def get(
|
|
67
|
+
self, key: _IdentityKeyType[_O], default: Optional[_O] = None
|
|
68
|
+
) -> Optional[_O]:
|
|
69
|
+
raise NotImplementedError()
|
|
70
|
+
|
|
71
|
+
def fast_get_state(
|
|
72
|
+
self, key: _IdentityKeyType[_O]
|
|
73
|
+
) -> Optional[InstanceState[_O]]:
|
|
74
|
+
raise NotImplementedError()
|
|
75
|
+
|
|
76
|
+
def keys(self) -> Iterable[_IdentityKeyType[Any]]:
|
|
77
|
+
return self._dict.keys()
|
|
78
|
+
|
|
79
|
+
def values(self) -> Iterable[object]:
|
|
80
|
+
raise NotImplementedError()
|
|
81
|
+
|
|
82
|
+
def replace(self, state: InstanceState[_O]) -> Optional[InstanceState[_O]]:
|
|
83
|
+
raise NotImplementedError()
|
|
84
|
+
|
|
85
|
+
def add(self, state: InstanceState[Any]) -> bool:
|
|
86
|
+
raise NotImplementedError()
|
|
87
|
+
|
|
88
|
+
def _fast_discard(self, state: InstanceState[Any]) -> None:
|
|
89
|
+
raise NotImplementedError()
|
|
90
|
+
|
|
91
|
+
def _add_unpresent(
|
|
92
|
+
self, state: InstanceState[Any], key: _IdentityKeyType[Any]
|
|
93
|
+
) -> None:
|
|
94
|
+
"""optional inlined form of add() which can assume item isn't present
|
|
95
|
+
in the map"""
|
|
96
|
+
self.add(state)
|
|
97
|
+
|
|
98
|
+
def _manage_incoming_state(self, state: InstanceState[Any]) -> None:
|
|
99
|
+
state._instance_dict = self._wr
|
|
100
|
+
|
|
101
|
+
if state.modified:
|
|
102
|
+
self._modified.add(state)
|
|
103
|
+
|
|
104
|
+
def _manage_removed_state(self, state: InstanceState[Any]) -> None:
|
|
105
|
+
del state._instance_dict
|
|
106
|
+
if state.modified:
|
|
107
|
+
self._modified.discard(state)
|
|
108
|
+
|
|
109
|
+
def _dirty_states(self) -> Set[InstanceState[Any]]:
|
|
110
|
+
return self._modified
|
|
111
|
+
|
|
112
|
+
def check_modified(self) -> bool:
|
|
113
|
+
"""return True if any InstanceStates present have been marked
|
|
114
|
+
as 'modified'.
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
return bool(self._modified)
|
|
118
|
+
|
|
119
|
+
def has_key(self, key: _IdentityKeyType[Any]) -> bool:
|
|
120
|
+
return key in self
|
|
121
|
+
|
|
122
|
+
def __len__(self) -> int:
|
|
123
|
+
return len(self._dict)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class WeakInstanceDict(IdentityMap):
|
|
127
|
+
_dict: Dict[_IdentityKeyType[Any], InstanceState[Any]]
|
|
128
|
+
|
|
129
|
+
def __getitem__(self, key: _IdentityKeyType[_O]) -> _O:
|
|
130
|
+
state = cast("InstanceState[_O]", self._dict[key])
|
|
131
|
+
o = state.obj()
|
|
132
|
+
if o is None:
|
|
133
|
+
raise KeyError(key)
|
|
134
|
+
return o
|
|
135
|
+
|
|
136
|
+
def __contains__(self, key: _IdentityKeyType[Any]) -> bool:
|
|
137
|
+
try:
|
|
138
|
+
if key in self._dict:
|
|
139
|
+
state = self._dict[key]
|
|
140
|
+
o = state.obj()
|
|
141
|
+
else:
|
|
142
|
+
return False
|
|
143
|
+
except KeyError:
|
|
144
|
+
return False
|
|
145
|
+
else:
|
|
146
|
+
return o is not None
|
|
147
|
+
|
|
148
|
+
def contains_state(self, state: InstanceState[Any]) -> bool:
|
|
149
|
+
if state.key in self._dict:
|
|
150
|
+
if TYPE_CHECKING:
|
|
151
|
+
assert state.key is not None
|
|
152
|
+
try:
|
|
153
|
+
return self._dict[state.key] is state
|
|
154
|
+
except KeyError:
|
|
155
|
+
return False
|
|
156
|
+
else:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
def replace(
|
|
160
|
+
self, state: InstanceState[Any]
|
|
161
|
+
) -> Optional[InstanceState[Any]]:
|
|
162
|
+
assert state.key is not None
|
|
163
|
+
if state.key in self._dict:
|
|
164
|
+
try:
|
|
165
|
+
existing = existing_non_none = self._dict[state.key]
|
|
166
|
+
except KeyError:
|
|
167
|
+
# catch gc removed the key after we just checked for it
|
|
168
|
+
existing = None
|
|
169
|
+
else:
|
|
170
|
+
if existing_non_none is not state:
|
|
171
|
+
self._manage_removed_state(existing_non_none)
|
|
172
|
+
else:
|
|
173
|
+
return None
|
|
174
|
+
else:
|
|
175
|
+
existing = None
|
|
176
|
+
|
|
177
|
+
self._dict[state.key] = state
|
|
178
|
+
self._manage_incoming_state(state)
|
|
179
|
+
return existing
|
|
180
|
+
|
|
181
|
+
def add(self, state: InstanceState[Any]) -> bool:
|
|
182
|
+
key = state.key
|
|
183
|
+
assert key is not None
|
|
184
|
+
# inline of self.__contains__
|
|
185
|
+
if key in self._dict:
|
|
186
|
+
try:
|
|
187
|
+
existing_state = self._dict[key]
|
|
188
|
+
except KeyError:
|
|
189
|
+
# catch gc removed the key after we just checked for it
|
|
190
|
+
pass
|
|
191
|
+
else:
|
|
192
|
+
if existing_state is not state:
|
|
193
|
+
o = existing_state.obj()
|
|
194
|
+
if o is not None:
|
|
195
|
+
raise sa_exc.InvalidRequestError(
|
|
196
|
+
"Can't attach instance "
|
|
197
|
+
"%s; another instance with key %s is already "
|
|
198
|
+
"present in this session."
|
|
199
|
+
% (orm_util.state_str(state), state.key)
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
return False
|
|
203
|
+
self._dict[key] = state
|
|
204
|
+
self._manage_incoming_state(state)
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
def _add_unpresent(
|
|
208
|
+
self, state: InstanceState[Any], key: _IdentityKeyType[Any]
|
|
209
|
+
) -> None:
|
|
210
|
+
# inlined form of add() called by loading.py
|
|
211
|
+
self._dict[key] = state
|
|
212
|
+
state._instance_dict = self._wr
|
|
213
|
+
|
|
214
|
+
def fast_get_state(
|
|
215
|
+
self, key: _IdentityKeyType[_O]
|
|
216
|
+
) -> Optional[InstanceState[_O]]:
|
|
217
|
+
return self._dict.get(key)
|
|
218
|
+
|
|
219
|
+
def get(
|
|
220
|
+
self, key: _IdentityKeyType[_O], default: Optional[_O] = None
|
|
221
|
+
) -> Optional[_O]:
|
|
222
|
+
if key not in self._dict:
|
|
223
|
+
return default
|
|
224
|
+
try:
|
|
225
|
+
state = cast("InstanceState[_O]", self._dict[key])
|
|
226
|
+
except KeyError:
|
|
227
|
+
# catch gc removed the key after we just checked for it
|
|
228
|
+
return default
|
|
229
|
+
else:
|
|
230
|
+
o = state.obj()
|
|
231
|
+
if o is None:
|
|
232
|
+
return default
|
|
233
|
+
return o
|
|
234
|
+
|
|
235
|
+
def items(self) -> List[Tuple[_IdentityKeyType[Any], InstanceState[Any]]]:
|
|
236
|
+
values = self.all_states()
|
|
237
|
+
result = []
|
|
238
|
+
for state in values:
|
|
239
|
+
value = state.obj()
|
|
240
|
+
key = state.key
|
|
241
|
+
assert key is not None
|
|
242
|
+
if value is not None:
|
|
243
|
+
result.append((key, value))
|
|
244
|
+
return result
|
|
245
|
+
|
|
246
|
+
def values(self) -> List[object]:
|
|
247
|
+
values = self.all_states()
|
|
248
|
+
result = []
|
|
249
|
+
for state in values:
|
|
250
|
+
value = state.obj()
|
|
251
|
+
if value is not None:
|
|
252
|
+
result.append(value)
|
|
253
|
+
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
def __iter__(self) -> Iterator[_IdentityKeyType[Any]]:
|
|
257
|
+
return iter(self.keys())
|
|
258
|
+
|
|
259
|
+
def all_states(self) -> List[InstanceState[Any]]:
|
|
260
|
+
return list(self._dict.values())
|
|
261
|
+
|
|
262
|
+
def _fast_discard(self, state: InstanceState[Any]) -> None:
|
|
263
|
+
# used by InstanceState for state being
|
|
264
|
+
# GC'ed, inlines _managed_removed_state
|
|
265
|
+
key = state.key
|
|
266
|
+
assert key is not None
|
|
267
|
+
try:
|
|
268
|
+
st = self._dict[key]
|
|
269
|
+
except KeyError:
|
|
270
|
+
# catch gc removed the key after we just checked for it
|
|
271
|
+
pass
|
|
272
|
+
else:
|
|
273
|
+
if st is state:
|
|
274
|
+
self._dict.pop(key, None)
|
|
275
|
+
|
|
276
|
+
def discard(self, state: InstanceState[Any]) -> None:
|
|
277
|
+
self.safe_discard(state)
|
|
278
|
+
|
|
279
|
+
def safe_discard(self, state: InstanceState[Any]) -> None:
|
|
280
|
+
key = state.key
|
|
281
|
+
if key in self._dict:
|
|
282
|
+
assert key is not None
|
|
283
|
+
try:
|
|
284
|
+
st = self._dict[key]
|
|
285
|
+
except KeyError:
|
|
286
|
+
# catch gc removed the key after we just checked for it
|
|
287
|
+
pass
|
|
288
|
+
else:
|
|
289
|
+
if st is state:
|
|
290
|
+
self._dict.pop(key, None)
|
|
291
|
+
self._manage_removed_state(state)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _killed(state: InstanceState[Any], key: _IdentityKeyType[Any]) -> NoReturn:
|
|
295
|
+
# external function to avoid creating cycles when assigned to
|
|
296
|
+
# the IdentityMap
|
|
297
|
+
raise sa_exc.InvalidRequestError(
|
|
298
|
+
"Object %s cannot be converted to 'persistent' state, as this "
|
|
299
|
+
"identity map is no longer valid. Has the owning Session "
|
|
300
|
+
"been closed?" % orm_util.state_str(state),
|
|
301
|
+
code="lkrp",
|
|
302
|
+
)
|