SQLAlchemy Raises None, Raises TypeError

I use a declarative extension in SQLAlchemy, and I noticed a strange error when I tried to save an instance of the mapped class with incorrect data (in particular, a column with a value of zero = False with a value of None).

Class (Simplified):

class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True, autoincrement=True) userid = Column(String(50), unique=True, nullable=False) 

Causing the error (session - SQLAlchemy session):

 >>> u = User() >>> session.add(u) >>> session.commit() ... TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType 

Looking at the code that throws this exception, I found (in sqlalchemy.orm.session):

 except: transaction.rollback(_capture_exception=True) raise 

The exception in this case is sqlalchemy.exc.OperationalError. If I changed these lines to:

 except Exception as e: transaction.rollback(_capture_exception=True) raise e 

then the problem will disappear, and instead of OperateError, None will be selected. Shouldn't source code work in any new version of Python? (I am using 2.7.2) Is this error somehow specific to my application?

Python 2.7.2

SQLAlchemy 0.7.5

UPDATE: the error seems to be specific to my application. I am wrapping eventlet.db_pool with an SQLAlchemy engine, which somehow seems to be the source of the problem. Running my simple test with SQLite or the underlying MySQL engine in memory does not have this problem, but with db_pool it does.

Test case: https://gist.github.com/1980584

Full trace:

 Traceback (most recent call last): File "test_case_9525220.py", line 41, in <module> session.commit() File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 645, in commit self.transaction.commit() File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 313, in commit self._prepare_impl() File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 297, in _prepare_impl self.session.flush() File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1547, in flush self._flush(objects) File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1635, in _flush raise TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType 
+7
source share
1 answer

Here is what I discovered:

  • The exception (a OperationalError ) is normal until the failed transaction returns (in Session._flush() ).
  • The transaction rollback is processed by mysqldb through eventlet.tpool . In particular, eventlet.tpool.execute is eventlet.tpool.execute , which involves creating an eventlet.Event and calling its wait method.
  • When waiting, several complex events related to the flow occur, one of which checks the exception and passes it to the event being processed. It raises an OperationalError , which is still in sys.exc_type , and ultimately clears it in eventlet.event.hubs.hub.BaseHub.switch .
  • The control returns to Session._flush , and the exception is re-thrown (using raise ), but there is no exception at this point, so it tries to raise None .

This behavior can be reproduced with a simple example:

 from eventlet import tpool def m(): pass try: raise TypeError except: tpool.execute(m) raise 

It’s a bit unclear which eventlet should do in this situation, so I don’t know whether to report an error in sqlalchemy or eventlet, or both.

The easiest way to fix this, as you already noted, is to change the last few lines of sqlalchemy.orm.session.Session._flush from

  except Exception: transaction.rollback(_capture_exception=True) raise 

to

  except Exception, e: transaction.rollback(_capture_exception=True) raise e 

Edit: I raised issue on the tray about event issues. It might also be worth upgrading it to sqlalchemy.

+5
source

All Articles