From 3aed5fa544685a41c0486b39dfaeb98ac69be4af Mon Sep 17 00:00:00 2001 From: Jason Kirtland Date: Thu, 31 Jan 2008 19:48:13 +0000 Subject: [PATCH] - Friendlier exception messages for unbound, implicit execution - Implicit binding failures now raise UnboundExecutionError --- lib/sqlalchemy/engine/base.py | 2 +- lib/sqlalchemy/exceptions.py | 2 ++ lib/sqlalchemy/orm/dynamic.py | 2 +- lib/sqlalchemy/orm/mapper.py | 2 +- lib/sqlalchemy/orm/session.py | 6 ++-- lib/sqlalchemy/orm/strategies.py | 4 +-- lib/sqlalchemy/schema.py | 20 +++++++++-- lib/sqlalchemy/sql/expression.py | 8 ++++- test/engine/bind.py | 59 +++++++++++++++++++++++++++++--- 9 files changed, 89 insertions(+), 16 deletions(-) diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 733d77a696..2bbbf398d6 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -484,7 +484,7 @@ class Compiled(object): e = self.bind if e is None: - raise exceptions.InvalidRequestError("This Compiled object is not bound to any Engine or Connection.") + raise exceptions.UnboundExecutionError("This Compiled object is not bound to any Engine or Connection.") return e._execute_compiled(self, multiparams, params) def scalar(self, *multiparams, **params): diff --git a/lib/sqlalchemy/exceptions.py b/lib/sqlalchemy/exceptions.py index eda368d7cc..7bac05c26f 100644 --- a/lib/sqlalchemy/exceptions.py +++ b/lib/sqlalchemy/exceptions.py @@ -55,6 +55,8 @@ class NoSuchTableError(InvalidRequestError): database, but the table doesn't exist. """ +class UnboundExecutionError(InvalidRequestError): + """SQL was attempted without a database connection to execute it on.""" class AssertionError(SQLAlchemyError): """Corresponds to internal state being detected in an invalid state.""" diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py index a5e1a84678..98702ecb04 100644 --- a/lib/sqlalchemy/orm/dynamic.py +++ b/lib/sqlalchemy/orm/dynamic.py @@ -128,7 +128,7 @@ class AppenderQuery(Query): try: sess = object_mapper(instance).get_session() except exceptions.InvalidRequestError: - raise exceptions.InvalidRequestError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (mapperutil.instance_str(instance), self.attr.key)) + raise exceptions.UnboundExecutionError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (mapperutil.instance_str(instance), self.attr.key)) return sess.query(self.attr.target_mapper).with_parent(instance, self.attr.key) diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 1489ef65e1..b99e736b3e 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -1555,7 +1555,7 @@ def _load_scalar_attributes(instance, attribute_names): try: session = mapper.get_session() except exceptions.InvalidRequestError: - raise exceptions.InvalidRequestError("Instance %s is not bound to a Session, and no contextual session is established; attribute refresh operation cannot proceed" % (instance.__class__)) + raise exceptions.UnboundExecutionError("Instance %s is not bound to a Session, and no contextual session is established; attribute refresh operation cannot proceed" % (instance.__class__)) state = instance._state if '_instance_key' in state.dict: diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index b689b24411..c75b786644 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -695,7 +695,7 @@ class Session(object): if self.bind is not None: return self.bind else: - raise exceptions.InvalidRequestError("This session is unbound to any Engine or Connection; specify a mapper to get_bind()") + raise exceptions.UnboundExecutionError("This session is unbound to any Engine or Connection; specify a mapper to get_bind()") elif len(self.__binds): if mapper is not None: @@ -713,7 +713,7 @@ class Session(object): if self.bind is not None: return self.bind elif mapper is None: - raise exceptions.InvalidRequestError("Could not locate any mapper associated with SQL expression") + raise exceptions.UnboundExecutionError("Could not locate any mapper associated with SQL expression") else: if isinstance(mapper, type): mapper = _class_mapper(mapper) @@ -721,7 +721,7 @@ class Session(object): mapper = mapper.compile() e = mapper.mapped_table.bind if e is None: - raise exceptions.InvalidRequestError("Could not locate any Engine or Connection bound to mapper '%s'" % str(mapper)) + raise exceptions.UnboundExecutionError("Could not locate any Engine or Connection bound to mapper '%s'" % str(mapper)) return e def query(self, mapper_or_class, *addtl_entities, **kwargs): diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 908c43feb1..3b3c86d1a6 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -192,7 +192,7 @@ class LoadDeferredColumns(object): session = sessionlib.object_session(self.instance) if session is None: - raise exceptions.InvalidRequestError("Parent instance %s is not bound to a Session; deferred load operation of attribute '%s' cannot proceed" % (self.instance.__class__, self.key)) + raise exceptions.UnboundExecutionError("Parent instance %s is not bound to a Session; deferred load operation of attribute '%s' cannot proceed" % (self.instance.__class__, self.key)) query = session.query(localparent) if not self.optimizing_statement: @@ -438,7 +438,7 @@ class LoadLazyAttribute(object): try: session = instance_mapper.get_session() except exceptions.InvalidRequestError: - raise exceptions.InvalidRequestError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (instance.__class__, self.key)) + raise exceptions.UnboundExecutionError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (instance.__class__, self.key)) q = session.query(prop.mapper).autoflush(False) if self.path: diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 0d626397f3..44dcb57555 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -1336,5 +1336,21 @@ class SchemaVisitor(visitors.ClauseVisitor): def _bind_or_error(schemaitem): bind = schemaitem.bind if not bind: - raise exceptions.InvalidRequestError("This SchemaItem is not connected to any Engine or Connection.") - return bind \ No newline at end of file + name = schemaitem.__class__.__name__ + label = getattr(schemaitem, 'fullname', + getattr(schemaitem, 'name', None)) + if label: + item = '%s %r' % (name, label) + else: + item = name + if isinstance(schemaitem, MetaData): + bindable = "the %s's .bind" % name + else: + bindable = "this %s's .metadata.bind" % name + + msg = ('The %s is not bound to an Engine or Connection. ' + 'Execution can not proceed without a database to execute ' + 'against. Either execute with an explicit connection or ' + 'assign %s to enable implicit execution.') % (item, bindable) + raise exceptions.UnboundExecutionError(msg) + return bind diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 2e5797b346..ee867a2f9c 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -1002,7 +1002,13 @@ class ClauseElement(object): e = self.bind if e is None: - raise exceptions.InvalidRequestError("This Compiled object is not bound to any Engine or Connection.") + label = getattr(self, 'description', self.__class__.__name__) + msg = ('This %s is not bound to an Engine or Connection. ' + 'Execution can not proceed without a database to execute ' + 'against. Either execute with an explicit connection or ' + 'bind the MetaData of the underlying tables to enable ' + 'implicit execution.') % label + raise exceptions.UnboundExecutionError(msg) return e.execute_clauseelement(self, multiparams, params) def scalar(self, *multiparams, **params): diff --git a/test/engine/bind.py b/test/engine/bind.py index 7e716ba9d8..c273bdb05e 100644 --- a/test/engine/bind.py +++ b/test/engine/bind.py @@ -34,7 +34,6 @@ class BindTest(PersistTest): for meth in [ metadata.create_all, - table.exists, metadata.drop_all, table.create, table.drop, @@ -42,8 +41,53 @@ class BindTest(PersistTest): try: meth() assert False - except exceptions.InvalidRequestError, e: - assert str(e) == "This SchemaItem is not connected to any Engine or Connection." + except exceptions.UnboundExecutionError, e: + self.assertEquals( + str(e), + "The MetaData " + "is not bound to an Engine or Connection. " + "Execution can not proceed without a database to execute " + "against. Either execute with an explicit connection or " + "assign the MetaData's .bind to enable implicit execution.") + + for meth in [ + table.exists, + # future: + #table.create, + #table.drop, + ]: + try: + meth() + assert False + except exceptions.UnboundExecutionError, e: + self.assertEquals( + str(e), + "The Table 'test_table' " + "is not bound to an Engine or Connection. " + "Execution can not proceed without a database to execute " + "against. Either execute with an explicit connection or " + "assign this Table's .metadata.bind to enable implicit " + "execution.") + + @testing.future + def test_create_drop_err2(self): + for meth in [ + table.exists, + table.create, + table.drop, + ]: + try: + meth() + assert False + except exceptions.UnboundExecutionError, e: + self.assertEquals( + str(e), + "The Table 'test_table' " + "is not bound to an Engine or Connection. " + "Execution can not proceed without a database to execute " + "against. Either execute with an explicit connection or " + "assign this Table's .metadata.bind to enable implicit " + "execution.") @testing.uses_deprecated('//connect') def test_create_drop_bound(self): @@ -157,8 +201,13 @@ class BindTest(PersistTest): assert e.bind is None e.execute() assert False - except exceptions.InvalidRequestError, e: - assert str(e) == "This Compiled object is not bound to any Engine or Connection." + except exceptions.UnboundExecutionError, e: + assert str(e).endswith( + 'is not bound to an Engine or ' + 'Connection. Execution can not proceed without a ' + 'database to execute against. Either execute with ' + 'an explicit connection or bind the MetaData of the ' + 'underlying tables to enable implicit execution.') finally: if isinstance(bind, engine.Connection): -- 2.47.3