Your exception block *is* catching an exception, but that's not the exception you are seeing being logged. Imagine the code in pyramid_tm does something like this (where "handler" is the view function, in your case exception_test):
# Actual code is at # https://github.com/Pylons/pyramid_tm/blob/master/pyramid_tm/__init__.py#L44 # this is just a trivial example def manage_transaction(request): transaction = start_transaction() try: response = handler(request) except Exception, e: transaction.rollback() else: transaction.commit() return response In your exception_test function, you've caught the IntegrityError and returned a dictionary. pyramid_tm has no idea that an exception even occurred, so control falls through to the "else" clause which tries to commit the transaction. This fails because the transaction is in a broken state, so a *new exception* is raised. You can't catch the new exception, because your code isn't running any more. Your view function has already returned. Simon On Mon, Mar 21, 2016 at 10:51 AM, Zsolt Ero <zsolt....@gmail.com> wrote: > I'll try to make a minimal reproducible sample of out if. > > For autoflush, I tried putting manual flush lines between the commands > but the results were the same. > > About exception, it is entering the exception block (I tried putting > print 'exception' there) but it is not catching it. > > Zsolt > > On 21 March 2016 at 11:20, Simon King <si...@simonking.org.uk> wrote: > > On Sat, Mar 19, 2016 at 3:44 AM, Zsolt Ero <zsolt....@gmail.com> wrote: > >> > >> My project is based on standard SQLAlchemy scaffold, ZTE, using ORM. > >> Pyramid 1.5.8. > >> > >> I'm trying to catch an IntegrityError (unique key constraint), I've > made a > >> very minimal example > >> > >> @view_config(route_name='exception_test', renderer='json') > >> def exception_test(request): > >> user = DBSession.query(User).filter(User.id == 1).first() > >> user.tutorial_done = True > >> > >> DBSession.query(Map).first() > >> > >> map_obj = Map(user=request.user) > >> map_obj.name = u'a' > >> map_obj.slug = 'a' > >> > >> try: > >> DBSession.add(map_obj) > >> DBSession.flush() > >> > >> except Exception: > >> return {'success': False} > >> > >> return { > >> 'id': map_obj.id, > >> 'success': True > >> } > >> > >> > >> This results in a InvalidRequestError, which cannot be catched, even > with > >> an except Exception. > >> > >> 04:34:25,180 ERROR [waitress] Exception when serving /exception_test > >> Traceback (most recent call last): > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/channel.py", > >> line 336, in service > >> task.service() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/task.py", > >> line 169, in service > >> self.execute() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/task.py", > >> line 388, in execute > >> app_iter = self.channel.server.application(env, start_response) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/paste/translogger.py", > >> line 69, in __call__ > >> return self.application(environ, replacement_start_response) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/router.py", > >> line 242, in __call__ > >> response = self.invoke_subrequest(request, use_tweens=True) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/router.py", > >> line 217, in invoke_subrequest > >> response = handle_request(request) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/tweens.py", > >> line 21, in excview_tween > >> response = handler(request) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid_tm/__init__.py", > >> line 101, in tm_tween > >> reraise(*exc_info) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid_tm/__init__.py", > >> line 90, in tm_tween > >> manager.commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_manager.py", > >> line 111, in commit > >> return self.get().commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 280, in commit > >> reraise(t, v, tb) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 271, in commit > >> self._commitResources() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 417, in _commitResources > >> reraise(t, v, tb) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 394, in _commitResources > >> rm.tpc_vote(self) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/zope/sqlalchemy/datamanager.py", > >> line 103, in tpc_vote > >> self.tx.commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > >> line 390, in commit > >> self._assert_active(prepared_ok=True) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > >> line 214, in _assert_active > >> % self._rollback_exception > >> InvalidRequestError: This Session's transaction has been rolled back due > >> to a previous exception during flush. To begin a new transaction with > this > >> Session, first issue Session.rollback(). Original exception was: > >> (psycopg2.IntegrityError) duplicate key value violates unique constraint > >> "uq_maps_user_id" > >> DETAIL: Key (user_id, slug)=(1, a) already exists. > >> > >> If I put a DBSession.rollback() in the exception block, it results in: > >> > >> 04:37:58,625 ERROR [waitress] Exception when serving /exception_test > >> Traceback (most recent call last): > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/channel.py", > >> line 336, in service > >> task.service() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/task.py", > >> line 169, in service > >> self.execute() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/waitress/task.py", > >> line 388, in execute > >> app_iter = self.channel.server.application(env, start_response) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/paste/translogger.py", > >> line 69, in __call__ > >> return self.application(environ, replacement_start_response) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/router.py", > >> line 242, in __call__ > >> response = self.invoke_subrequest(request, use_tweens=True) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/router.py", > >> line 217, in invoke_subrequest > >> response = handle_request(request) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid/tweens.py", > >> line 21, in excview_tween > >> response = handler(request) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid_tm/__init__.py", > >> line 101, in tm_tween > >> reraise(*exc_info) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/pyramid_tm/__init__.py", > >> line 90, in tm_tween > >> manager.commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_manager.py", > >> line 111, in commit > >> return self.get().commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 280, in commit > >> reraise(t, v, tb) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 271, in commit > >> self._commitResources() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 417, in _commitResources > >> reraise(t, v, tb) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/transaction/_transaction.py", > >> line 394, in _commitResources > >> rm.tpc_vote(self) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/zope/sqlalchemy/datamanager.py", > >> line 103, in tpc_vote > >> self.tx.commit() > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > >> line 390, in commit > >> self._assert_active(prepared_ok=True) > >> File > >> > "/Users/user/.virtualenvs/maphub_web/lib/python2.7/site-packages/sqlalchemy/orm/session.py", > >> line 223, in _assert_active > >> raise sa_exc.ResourceClosedError(closed_msg) > >> ResourceClosedError: This transaction is closed > >> > >> > >> Moreover, the weird part is that it all seems to depend on seemingly > >> meaningless (to me at least) order of the queries: > >> > >> Moving the query(Map) to be before user.tutorial_done > >> > >> user = DBSession.query(User).filter(User.id == 1).first() > >> DBSession.query(Map).first() > >> user.tutorial_done = True > >> > >> Results in the correct, catchable IntegrityError. > >> > >> However, removing the .filter() part makes it broken again: > >> > >> user = DBSession.query(User).first() > >> DBSession.query(Map).first() > >> user.tutorial_done = True > >> > >> What is happening here? I'm totally confused. Is this a bug in ZTE or > >> SQLAlchemy? How can I possibly run into an uncatchable exception in a > view, > >> and if I do, what am I supposed to do? > > > > > > It's difficult to tell without a sample that we can run, but I think the > > exception is being raised after your view function returns. The zope > > transaction package (in combination with the pyramid_tm and > > pyramid_sqlalchemy packages) wraps each request to ensure that a > transaction > > is either committed or rolled back at the end of the request. Your > > IntegrityError in the middle of the view is causing the transaction to be > > left in a broken state, but you aren't signalling that to the transaction > > manager. It has no way of knowing that an error has occurred in the > view, so > > it tries to commit the transaction. Since this happens outside your view > > altogether, you can't catch it. > > > > The reason you get different behaviour when you order the statements > > differently is likely to be autoflush. By default, pending changes are > > flushed to the database whenever you issue a query (so in your original > > example, the call to DBSession.query(Map).first() will flush the > > user.tutorial_done change to the database). > > > > Hope that helps, > > > > Simon > > > > -- > > You received this message because you are subscribed to a topic in the > > Google Groups "pylons-devel" group. > > To unsubscribe from this topic, visit > > https://groups.google.com/d/topic/pylons-devel/NqHlZ52NPxU/unsubscribe. > > To unsubscribe from this group and all its topics, send an email to > > pylons-devel+unsubscr...@googlegroups.com. > > To post to this group, send email to pylons-devel@googlegroups.com. > > Visit this group at https://groups.google.com/group/pylons-devel. > > For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups > "pylons-devel" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to pylons-devel+unsubscr...@googlegroups.com. > To post to this group, send email to pylons-devel@googlegroups.com. > Visit this group at https://groups.google.com/group/pylons-devel. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "pylons-devel" group. To unsubscribe from this group and stop receiving emails from it, send an email to pylons-devel+unsubscr...@googlegroups.com. To post to this group, send email to pylons-devel@googlegroups.com. Visit this group at https://groups.google.com/group/pylons-devel. For more options, visit https://groups.google.com/d/optout.