]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Now uses sqlalchemy.orm.exc.DetachedInstanceError when an
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 7 Feb 2010 23:58:17 +0000 (23:58 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 7 Feb 2010 23:58:17 +0000 (23:58 +0000)
attribute load or refresh action fails due to object
being detached from any Session.   UnboundExecutionError
is specific to engines bound to sessions and statements.

CHANGES
lib/sqlalchemy/orm/dynamic.py
lib/sqlalchemy/orm/exc.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/strategies.py
test/orm/test_expire.py
test/orm/test_lazy_relations.py

diff --git a/CHANGES b/CHANGES
index d88a6bfdc8a8bcd824430ffee5e4e7b35fb6a830..5ecb3c4dd5169dd1308ee45a529b1e0a7bb71801 100644 (file)
--- 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.
index 456bcd34e67bae9a4cbc403c0f761a1b064fbaa1..308d69fe8a70c527cae30bdc0e204b9da007ae58 100644 (file)
@@ -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" % (
index 8b52eec8ac35b2e34565f977cae4b1a7b9c64cc9..431acc15cc9e3d8cb72432cb3387b4c2cf2b36ef 100644 (file)
@@ -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."""
 
index 2bdf2b84764e13634062fbc74f24abbd155b4681..63ad20bcac55c1e954663a0f8791725efd77682a 100644 (file)
@@ -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))
index 0970f383623a2d1aea6c4853956cbaab1b5a64f4..33e60491a1dc0219f01d326f0fc94aa251402edd 100644 (file)
@@ -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)
index c602ac963f3607592f36d8f90018e0452a47bf36..c8ce5c7dfe58eb620aa605756775814e630dbcea 100644 (file)
@@ -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):
index 1104ef1e0a974bbb2e0c08a73d27a3ec5f1407f5..ac175c83bc119b5033fc5734546e777aacfd7a35 100644 (file)
@@ -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):