my user is interacting with a gtk2 GUI (Presenter/View/Model), and I want to let them import several records in the database, keep the interface responsive, let the user cancel the operation at any time, and when the longish operation is complete, let the user decide whether or not to commit the whole import.
I'm doing this by running the import action in a separate thread, the GUI stays responsive, in the separate thread I create database objects, add them to the session (owned by the presenter), the importing thread adds log records to a gtk.ListStore
, linked to a gtk.TreeView
, and this is fine as far as the GUI is concerned. When the thread finalises, the 'ok' button is enabled.
Since this is part of a fairly large program I've adopted, there's settings I can't change without reviewing the whole lot. One of them is autoflush, which is disabled in all sessions.
now I have this smallish example with a surprising behaviour:
from sqlalchemy import Column, Unicode, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Location(Base):
__tablename__ = 'location'
code = Column(Unicode(64), index=True, primary_key=True)
def __init__(self, code=None):
self.code = code
def __repr__(self):
return self.code
from sqlalchemy import create_engine
engine = create_engine('sqlite:///joindemo.db')
Base.metadata.create_all(engine)
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()
session.query(Location).delete()
session.commit()
us = session.query(Location).all()
print us
def run_me_in_other_thread_1():
session.autoflush = True
u1 = Location(code=u'mario')
u2 = Location(code=u'silla')
session.add_all([u1, u2])
session.autoflush = False
def run_me_in_other_thread_2():
u1 = Location(code=u'mario')
u2 = Location(code=u'silla')
session.add_all([u1, u2])
session.flush()
import threading
t = threading.Thread(target=run_me_in_other_thread_1)
t.start()
t.join()
session.commit()
us = session.query(Location).all()
print us
If you use as thread target run_me_in_other_thread_1
(temporarily enable autoflush in the session), all goes as I also expected:
[]
[mario, silla]
(Unfortunately, enabling autoflush, even just for one import, is something I cannot do within ghini/bauble and I don't have the energies to look into that. Needless to say, if you want to have a look, fork the project and remove that nasty autoflush=False
, and manage to solve those 10 consequent erroring tests, be my guest! I'll be glad to add new contributors to the authors list.)
If you leave autoflush disabled and do a session.flush
before returning from the target function (see run_me_in_other_thread_2
), the flush will cause a ProgrammingError:
ProgrammingError: (sqlite3.ProgrammingError) SQLite objects created in a thread can only be used in that same thread.The object was created in thread id 140403696158464 and this is thread id 140403623274240
why this difference?