summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-02-07 23:58:17 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2010-02-07 23:58:17 +0000
commit03167fb4bbb2d4a1d68ef04dff02ed2033b4f138 (patch)
tree858b2e36b83467b8f4c2a3b72b9e4fc4bd7cadaa
parent52ab4041affc8618892d82d82281eedc5b3d0b6f (diff)
downloadsqlalchemy-03167fb4bbb2d4a1d68ef04dff02ed2033b4f138.tar.gz
- 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.
-rw-r--r--CHANGES5
-rw-r--r--lib/sqlalchemy/orm/dynamic.py5
-rw-r--r--lib/sqlalchemy/orm/exc.py4
-rw-r--r--lib/sqlalchemy/orm/mapper.py18
-rw-r--r--lib/sqlalchemy/orm/strategies.py8
-rw-r--r--test/orm/test_expire.py10
-rw-r--r--test/orm/test_lazy_relations.py4
7 files changed, 31 insertions, 23 deletions
diff --git a/CHANGES b/CHANGES
index d88a6bfdc..5ecb3c4dd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,11 @@ CHANGES
needless updates of the primary key column during a so-called
"row switch" operation, i.e. add + delete of two objects
with the same PK.
+
+ - 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.
- sql
- Added math negation operator support, -x.
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)
diff --git a/test/orm/test_expire.py b/test/orm/test_expire.py
index c602ac963..c8ce5c7df 100644
--- a/test/orm/test_expire.py
+++ b/test/orm/test_expire.py
@@ -7,7 +7,7 @@ from sqlalchemy.test import testing
from sqlalchemy import Integer, String, ForeignKey, exc as sa_exc
from sqlalchemy.test.schema import Table
from sqlalchemy.test.schema import Column
-from sqlalchemy.orm import mapper, relation, create_session, attributes, deferred
+from sqlalchemy.orm import mapper, relation, create_session, attributes, deferred, exc as orm_exc
from test.orm import _base, _fixtures
@@ -59,7 +59,7 @@ class ExpireTest(_fixtures.FixtureTest):
u = s.query(User).get(7)
s.expunge_all()
- assert_raises_message(sa.exc.InvalidRequestError, r"is not persistent within this Session", s.expire, u)
+ assert_raises_message(sa_exc.InvalidRequestError, r"is not persistent within this Session", s.expire, u)
@testing.resolve_artifact_names
def test_get_refreshes(self):
@@ -190,7 +190,7 @@ class ExpireTest(_fixtures.FixtureTest):
sess.expire(u, attribute_names=['name'])
sess.expunge(u)
- assert_raises(sa.exc.UnboundExecutionError, getattr, u, 'name')
+ assert_raises(orm_exc.DetachedInstanceError, getattr, u, 'name')
@testing.resolve_artifact_names
def test_pending_raises(self):
@@ -200,7 +200,7 @@ class ExpireTest(_fixtures.FixtureTest):
sess = create_session()
u = User(id=15)
sess.add(u)
- assert_raises(sa.exc.InvalidRequestError, sess.expire, u, ['name'])
+ assert_raises(sa_exc.InvalidRequestError, sess.expire, u, ['name'])
@testing.resolve_artifact_names
def test_no_instance_key(self):
@@ -843,7 +843,7 @@ class RefreshTest(_fixtures.FixtureTest):
s = create_session()
u = s.query(User).get(7)
s.expunge_all()
- assert_raises_message(sa.exc.InvalidRequestError, r"is not persistent within this Session", lambda: s.refresh(u))
+ assert_raises_message(sa_exc.InvalidRequestError, r"is not persistent within this Session", lambda: s.refresh(u))
@testing.resolve_artifact_names
def test_refresh_expired(self):
diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py
index 1104ef1e0..ac175c83b 100644
--- a/test/orm/test_lazy_relations.py
+++ b/test/orm/test_lazy_relations.py
@@ -3,7 +3,7 @@
from sqlalchemy.test.testing import assert_raises, assert_raises_message
import datetime
from sqlalchemy import exc as sa_exc
-from sqlalchemy.orm import attributes
+from sqlalchemy.orm import attributes, exc as orm_exc
import sqlalchemy as sa
from sqlalchemy.test import testing
from sqlalchemy import Integer, String, ForeignKey, SmallInteger
@@ -39,7 +39,7 @@ class LazyTest(_fixtures.FixtureTest):
q = sess.query(User)
u = q.filter(users.c.id == 7).first()
sess.expunge(u)
- assert_raises(sa_exc.InvalidRequestError, getattr, u, 'addresses')
+ assert_raises(orm_exc.DetachedInstanceError, getattr, u, 'addresses')
@testing.resolve_artifact_names
def test_orderby(self):