I'm updating Pyramid/SQLAlchemy legacy code to Python 3.8 from an app working fine under Python 2.7, and running it locally. All the necessary requirements are pip installed and setup.py runs without error.
On running initialise
with my local .ini file, All goes well, the database tables (MariaDB) are all written.
in models.py
from sqlalchemy.orm import (
scoped_session,
sessionmaker,
relationship,
backref,
synonym,
)
from zope.sqlalchemy import ZopeTransactionEvents
#[...]
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionEvents()))
in the main app it fails with 'ZopeTransactionEvents' object has no attribute 'after_commit'
at this function, after getting the final input and attempting to add it to the DB at DBSession.add(user)
:
def do_create_admin_user(self):
from ..models import User
from getpass import getpass
print("Create an administrative user")
fullname = input("full name: ")
username = input("username: ")
if not username:
self.log.info("missing username - aborted")
return
if len(username) > 50:
self.log.info("username too long - aborted")
return
password = getpass("password for {}: ".format(username))
with transaction.manager:
user = User(
username=username,
fullname=fullname,
administrator=True,
password=password
)
DBSession.add(user)
self.log.info("{} created".format(username))
Here are the two key parts of the stack trace:
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'
This specific issue halted the process, and despite days of research (and some unproductive hacking) I'm no closer to a solution. This is a legacy project and I'm not previously familiar with Pyramid or SQAlchemy, so finding my way as I go along.
In the end, this is what worked i.e. no arguments to sessionmaker()
from zope.sqlalchemy import register
# ...
DBSession = scoped_session(sessionmaker())
register(DBSession)
Now on to the next error.
This is due to a breaking change introduced in zope.sqlalchemy v1.2. See details in the zope.sqlalchemy pypi page
To make things clearer we renamed the ZopeTransactionExtension class to ZopeTransactionEvents. Existing code using the ‘register’ version stays compatible.
Your old code like this:
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), **options))
becomes:
from zope.sqlalchemy import register
DBSession = scoped_session(sessionmaker(**options))
register(DBSession)
Essentially its a minor breaking change to the zope.sqlalchemy
API that was introduced in version 1.2, this is now the way. Rather than registering the extension via keyword argument, you now register the session explicitly with the register
callable.
The above is a direct quote from the documentation. It may not be obvious but the use of **options
refers to optional keyword arguments. If you are not using any keyword arguments then this would be omitted (i.e. just call sessionmaker()
with no arguments).