Zope.sqalchemy: AttributeError: '_thread._local' object has no attribute 'value'

120 views
Skip to first unread message

Dave Everitt

unread,
Oct 24, 2020, 10:47:27 AM10/24/20
to sqlalchemy
I'm updating a working Pyramid app that uses sqlalchemy and have some success by replacing ZopeTransactionExtension with ZopeTransactionEvents.

On running initialise with my local .ini file, All goes well, the database tables (MariaDB) are all written, but these errors occur:

Traceback (most recent call last): "[...]sqlalchemy/util/_collections.py", line 1055, in __call__ return self.registry.value AttributeError: '_thread._local' object has no attribute 'value' During handling of the above exception, another exception occurred:

[cruft omitted]

"[...]sqlalchemy/orm/deprecated_interfaces.py", line 367, in _adapt_listener ls_meth = getattr(listener, meth) AttributeError: 'ZopeTransactionEvents' object has no attribute 'after_commit'

For more code details, I've posted extracts from the models and main app code on StackOverflow, but with no joy so far.

Jonathan Vanasco

unread,
Oct 24, 2020, 1:55:42 PM10/24/20
to sqlalchemy
The extract code you posted is incorrect.

You were given a step towards the right answer - you MUST invoke `register`.

I say a step, because there may be other factors going on.

However as you can see from the source code (https://github.com/zopefoundation/zope.sqlalchemy/blob/master/src/zope/sqlalchemy/datamanager.py#L293-L329), the call to `register` is required because it invokes the ZopeTransactionExtenstion AND sets up the transaction events.

Dave Everitt

unread,
Oct 25, 2020, 9:23:36 AM10/25/20
to sqlalchemy
Thanks for the pointer to the source. My confusion came from the Zope docs (and other sources e.g. this answer: https://stackoverflow.com/a/58567212/123033 ) that seemed to suggest EITHER
from zope.sqlalchemy import ZopeTransactionExtension, register
OR
changing all instances of ZopeTransactionExtension to ZopeTransactionEvents
and using:
from zope.sqlalchemy import register
then
DBSession = scoped_session(sessionmaker(**options))
but the below - i.e. no parameters to sessionmaker() - got past the errors in the end (so far so good, anyway):

from zope.sqlalchemy import register
# . . .
DBSession = scoped_session(sessionmaker())
register(DBSession)

Feel free to point out anything glaringly obvious. I've not been in this territory before, and it's a codebase in which I'm still finding my way (written by a codev) and yes, I might not spot what's taken for granted by anyone more familiar with SQLAlchemy etc. - I've often been in the reverse situation!

Jonathan Vanasco

unread,
Oct 25, 2020, 4:44:38 PM10/25/20
to sqlalchemy

Your new code is exactly what I have been running on several production systems, so it looks good to me!

Long story short, `zope.sqlalchemy` had been using the `sqlalchemy` "extensions", which were deprecated in 2012 and are set to be removed (if they haven't been already).  see https://github.com/zopefoundation/zope.sqlalchemy/issues/31

The change that caused your issues was due to `zope.sqlalchemy` migrating from the deprecated system to the next.  There wasn't a clean way of swapping this out, so their developers opted for a tiny breaking change.  For most people, that means changing two lines of code; in some complex projects, 4 lines of code might need to be changed!

Unfortunately, someone posted that answer on StackOverflow that is incorrect and misdirected you – it's not a simple change in class names.  I'm sorry that tripped you up.
Reply all
Reply to author
Forward
0 new messages