Gnu Mailman 3 uses SQL Alchemy to access SQL databases. We have an issue 
with one of or unit tests which passes with  SQLAlchemy <= 1.1.15 but which 
fails with SQLAlchemy >= 1.2. The failure is independent of the backend 
(sglite, mysql or pgsql). Here's a traceback from the failure.

Traceback (most recent call last):
  File "/builds/mailman/mailman/src/mailman/model/tests/test_user.py", line 
184, in test_absorb_memberships
    all_users = list(self._manager.users)
  File "/builds/mailman/mailman/src/mailman/model/usermanager.py", line 102, in 
users
    yield from store.query(User).order_by(User.id).all()
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/query.py",
 line 2726, in all
    return list(self)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/query.py",
 line 2877, in __iter__
    self.session._autoflush()
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/session.py",
 line 1434, in _autoflush
    self.flush()
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/session.py",
 line 2243, in flush
    self._flush(objects)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/session.py",
 line 2369, in _flush
    transaction.rollback(_capture_exception=True)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py",
 line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/util/compat.py",
 line 187, in reraise
    raise value
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/session.py",
 line 2333, in _flush
    flush_context.execute()
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py",
 line 391, in execute
    rec.execute(self)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py",
 line 542, in execute
    persistence.post_update(self.mapper, states, uow, cols)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py",
 line 234, in post_update
    mapper, table, update)
  File 
"/builds/mailman/mailman/.tox/py36-nocov/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py",
 line 982, in _emit_post_update_statements
    (table.description, len(records), rows))
sqlalchemy.orm.exc.StaleDataError: UPDATE statement on table 'user' expected to 
update 1 row(s); 0 were matched.


The test that fails sets up 3 mailing lists and subscribes 2 users (Anne 
and Bart) to some or all of the lists with different roles. Then user Anne 
"absorbs" user Bart so user Bart is deleted and user Anne assumes all 
Bart's memberships. The actual code in the test may not be intelligible 
without more Mailman knowledge, but it is:
    def test_absorb_memberships(self):
        # When a user is absorbed, all of their user-subscribed memberships
        # are relinked to the absorbing user.
        mlist2 = create_list('[email protected]')
        mlist3 = create_list('[email protected]')
        with transaction():
            # This has to happen in a transaction so that both the user and
            # the preferences objects get valid ids.
            bart = self._manager.create_user('[email protected]', 'Bart 
Person')
            set_preferred(bart)
        # Subscribe both users to self._mlist.
        self._mlist.subscribe(self._anne, MemberRole.member)
        self._mlist.subscribe(bart, MemberRole.moderator)
        # Subscribe only Bart to mlist2.
        mlist2.subscribe(bart, MemberRole.owner)
        # Subscribe only Bart's address to mlist3.
        mlist3.subscribe(bart.preferred_address, MemberRole.moderator)
        # There are now 4 memberships, one with Anne two with Bart's user 
and
        # one with Bart's address.
        all_members = list(self._manager.members)
        self.assertEqual(len(all_members), 4, all_members)
        # Do the absorption.
        self._anne.absorb(bart)
        # The Bart user has been deleted, leaving only the Anne user in the
        # user manager.
        all_users = list(self._manager.users)
        self.assertEqual(len(all_users), 1)
        self.assertEqual(all_users[0], self._anne)
        # There are no leftover memberships for user Bart.  Anne owns all 
the
        # memberships.
        all_members = list(self._manager.members)
        self.assertEqual(len(all_members), 4, all_members)
        self.assertEqual(self._anne.memberships.member_count, 4)
        memberships = {(member.list_id, member.role): member
                       for member in self._anne.memberships.members}
        # Note that Anne is now both a member and moderator of the test 
list.
        self.assertEqual(set(memberships), set([
            ('test.example.com', MemberRole.member),
            ('test.example.com', MemberRole.moderator),
            ('test2.example.com', MemberRole.owner),
            ('test3.example.com', MemberRole.moderator),
            ]))
        # Both of Bart's previous user subscriptions are now transferred to
        # the Anne user.
        self.assertEqual(
            memberships[('test.example.com', MemberRole.moderator)].address,
            self._anne.preferred_address)
        self.assertEqual(
            memberships[('test2.example.com', MemberRole.owner)].address,
            self._anne.preferred_address)
        # Bart's address was subscribed; it must not have been changed.  Of
        # course, Anne now controls [email protected].
        key = ('test3.example.com', MemberRole.moderator)
        self.assertEqual(memberships[key].address.email, '[email protected]')
        self.assertEqual(self._manager.get_user('[email protected]'),
                         self._anne)



The start of the traceback is the statement
        # The Bart user has been deleted, leaving only the Anne user in the
        # user manager.
        all_users = list(self._manager.users)
Versions older than 1.2 do not throw the sqlalchemy.orm.exc.StaleDataError 
exception at this point.

If I attempt to fix the test by doing the `self._anne.absorb(bart)` in a 
transaction that gets committed on completion, the same exception is thrown 
on the commit.

Does anyone have any ideas about this?

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to