From 03167fb4bbb2d4a1d68ef04dff02ed2033b4f138 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 7 Feb 2010 23:58:17 +0000 Subject: - Now uses sqlalchemy.orm.exc.DetachedInstanceError when an attribute load or refresh action fails due to object being detached from any Session. UnboundExecutionError is specific to engines bound to sessions and statements. --- lib/sqlalchemy/orm/dynamic.py | 5 +++-- lib/sqlalchemy/orm/exc.py | 4 +++- lib/sqlalchemy/orm/mapper.py | 18 +++++++++--------- lib/sqlalchemy/orm/strategies.py | 8 ++++---- 4 files changed, 19 insertions(+), 16 deletions(-) (limited to 'lib/sqlalchemy') diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py index 456bcd34e..308d69fe8 100644 --- a/lib/sqlalchemy/orm/dynamic.py +++ b/lib/sqlalchemy/orm/dynamic.py @@ -12,7 +12,8 @@ basic add/delete mutation. """ from sqlalchemy import log, util -import sqlalchemy.exceptions as sa_exc +from sqlalchemy import exc as sa_exc +from sqlalchemy.orm import exc as sa_exc from sqlalchemy.sql import operators from sqlalchemy.orm import ( attributes, object_session, util as mapperutil, strategies, object_mapper @@ -234,7 +235,7 @@ class AppenderMixin(object): if sess is None: sess = object_session(instance) if sess is None: - raise sa_exc.UnboundExecutionError( + raise orm_exc.DetachedInstanceError( "Parent instance %s is not bound to a Session, and no " "contextual session is established; lazy load operation " "of attribute '%s' cannot proceed" % ( diff --git a/lib/sqlalchemy/orm/exc.py b/lib/sqlalchemy/orm/exc.py index 8b52eec8a..431acc15c 100644 --- a/lib/sqlalchemy/orm/exc.py +++ b/lib/sqlalchemy/orm/exc.py @@ -23,7 +23,9 @@ class FlushError(sa.exc.SQLAlchemyError): class UnmappedError(sa.exc.InvalidRequestError): """TODO""" - +class DetachedInstanceError(sa.exc.SQLAlchemyError): + """An attempt to access unloaded attributes on a mapped instance that is detached.""" + class UnmappedInstanceError(UnmappedError): """An mapping operation was requested for an unknown instance.""" diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 2bdf2b847..63ad20bca 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -22,7 +22,7 @@ deque = __import__('collections').deque from sqlalchemy import sql, util, log, exc as sa_exc from sqlalchemy.sql import expression, visitors, operators, util as sqlutil -from sqlalchemy.orm import attributes, exc, sync +from sqlalchemy.orm import attributes, sync, exc as orm_exc from sqlalchemy.orm.interfaces import ( MapperProperty, EXT_CONTINUE, PropComparator ) @@ -1123,9 +1123,9 @@ class Mapper(object): except KeyError: prop = self._props.get(column.key, None) if prop: - raise exc.UnmappedColumnError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))) + raise orm_exc.UnmappedColumnError("Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))) else: - raise exc.UnmappedColumnError("No column %s is configured on mapper %s..." % (column, self)) + raise orm_exc.UnmappedColumnError("No column %s is configured on mapper %s..." % (column, self)) # TODO: improve names? def _get_state_attr_by_column(self, state, column): @@ -1319,7 +1319,7 @@ class Mapper(object): instance = uowtransaction.session.identity_map[instance_key] existing = attributes.instance_state(instance) if not uowtransaction.is_deleted(existing): - raise exc.FlushError( + raise orm_exc.FlushError( "New instance %s with identity key %s conflicts with persistent instance %s" % (state_str(state), instance_key, state_str(existing))) if self._should_log_debug: @@ -1460,7 +1460,7 @@ class Mapper(object): if connection.dialect.supports_sane_rowcount: if rows != len(update): - raise exc.ConcurrentModificationError( + raise orm_exc.ConcurrentModificationError( "Updated rowcount %d does not match number of objects updated %d" % (rows, len(update))) @@ -1600,7 +1600,7 @@ class Mapper(object): statement = table.delete(clause) c = connection.execute(statement, del_objects) if c.supports_sane_multi_rowcount() and c.rowcount != len(del_objects): - raise exc.ConcurrentModificationError("Deleted rowcount %d does not match " + raise orm_exc.ConcurrentModificationError("Deleted rowcount %d does not match " "number of objects deleted %d" % (c.rowcount, len(del_objects))) for state, mapper, connection in tups: @@ -1725,7 +1725,7 @@ class Mapper(object): if not currentload and version_id_col is not None and context.version_check and \ self._get_state_attr_by_column(state, self.version_id_col) != row[version_id_col]: - raise exc.ConcurrentModificationError( + raise orm_exc.ConcurrentModificationError( "Instance '%s' version of %s does not match %s" % (state_str(state), self._get_state_attr_by_column(state, self.version_id_col), row[version_id_col])) elif refresh_state: @@ -1928,7 +1928,7 @@ def _load_scalar_attributes(state, attribute_names): mapper = _state_mapper(state) session = _state_session(state) if not session: - raise sa_exc.UnboundExecutionError("Instance %s is not bound to a Session; " + raise orm_exc.DetachedInstanceError("Instance %s is not bound to a Session; " "attribute refresh operation cannot proceed" % (state_str(state))) has_key = _state_has_identity(state) @@ -1948,4 +1948,4 @@ def _load_scalar_attributes(state, attribute_names): # if instance is pending, a refresh operation may not complete (even if PK attributes are assigned) if has_key and result is None: - raise exc.ObjectDeletedError("Instance '%s' has been deleted." % state_str(state)) + raise orm_exc.ObjectDeletedError("Instance '%s' has been deleted." % state_str(state)) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 0970f3836..33e60491a 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -6,11 +6,11 @@ """sqlalchemy.orm.interfaces.LoaderStrategy implementations, and related MapperOptions.""" -import sqlalchemy.exceptions as sa_exc +from sqlalchemy import exc as sa_exc from sqlalchemy import sql, util, log from sqlalchemy.sql import util as sql_util from sqlalchemy.sql import visitors, expression, operators -from sqlalchemy.orm import mapper, attributes, interfaces +from sqlalchemy.orm import mapper, attributes, interfaces, exc as orm_exc from sqlalchemy.orm.interfaces import ( LoaderStrategy, StrategizedOption, MapperOption, PropertyOption, serialize_path, deserialize_path, StrategizedProperty @@ -291,7 +291,7 @@ class LoadDeferredColumns(object): session = sessionlib._state_session(state) if session is None: - raise sa_exc.UnboundExecutionError( + raise orm_exc.DetachedInstanceError( "Parent instance %s is not bound to a Session; " "deferred load operation of attribute '%s' cannot proceed" % (mapperutil.state_str(state), self.key) @@ -556,7 +556,7 @@ class LoadLazyAttribute(object): session = sessionlib._state_session(state) if session is None: - raise sa_exc.UnboundExecutionError( + raise orm_exc.DetachedInstanceError( "Parent instance %s is not bound to a Session; " "lazy load operation of attribute '%s' cannot proceed" % (mapperutil.state_str(state), self.key) -- cgit v1.2.1