Как предотвратить LockError в ZODB FileStorage при доступе к объектам ZODB через вызовы функций (один процесс)

У меня проблемы с ZODB LockError, но я считаю, что работаю с базой данных только с одним процессом.

Моя цель для моего проекта состоит в том, чтобы иметь только две функции, load_project() и save_project(), которые позволяют пользователю загружать объект Project, работать с ним, а затем периодически сохранять его с помощью save_project() без необходимости непосредственного взаимодействия пользователя с ZODB. Вроде как они просто работают над документом и сохраняют по ходу дела.

Однако я заметил, что я постоянно получаю LockError: я могу загрузить объект, сохранить объект один раз, но затем я получаю LockError всякий раз, когда пытаюсь сохранить его впоследствии. Очевидно, я что-то сделал не так или не понимаю, как работает ZODB. Как предотвратить это LockError?

Все остальные проблемы с LockError на SO, по-видимому, связаны с несколькими процессами. Я взаимодействую с кодом через Jupyter Notebook, который, я считаю, представляет собой всего лишь один процесс. Когда я сталкиваюсь с LockError, я могу перезапустить ядро ​​(запустив тем самым новый процесс), загрузить проект и снова его сохранить. Но тогда я не могу сохранить его во второй раз.

Вот код двух функций, которые я импортирую из своего модуля functions:

def save_project(project: BIMProject):
    """
    Commits the project to the ZODB; including opening and closing the connection
    """
    if not hasattr(project, "filename"):
        project.filename = input("Enter a filename (without extension):")
    if not hasattr(project, "filepath"):
        tk_root = Tk()
        tk_root.withdraw()
        project.filepath = filedialog.askdirectory(title="Please select a directory")

    filename = project.filename
    full_path = project.filepath + "/" + filename

    storage=FileStorage(full_path)
    db=ZODB.DB(storage)
    connection=db.open()
    root=connection.root()
    if 'project' in root:
        root.update({'project': project})
    else:
        root['project'] = project # reassign to change
    transaction.commit()
    connection.close()

def load_project():
    """
    Loads a project from a database file
    """
    tk_root = Tk()
    tk_root.withdraw()
    filepath = filedialog.askopenfilename(title="Please select a database file")

    storage = FileStorage(filepath)
    db = ZODB.DB(storage)
    connection = db.open()
    root = connection.root()
    project = copy.deepcopy(root["project"])
    connection.close()
    db.close()
    return project

Любая помощь будет принята с благодарностью.


person Connor Ferster    schedule 08.01.2019    source источник


Ответы (1)


Итак, после дня битья головой о стену, я прогулялся и решил, что вместо доступа к базе данных только через функции лучше подойдет объект для поддержания подключения к файловому хранилищу и базе данных. Не совсем то, что я себе представлял, но работает отлично.

class LoaderSaver:
    """
    A 'hang around' object to use in conjunction with Project objects to save them
    in a ZODB FileStorage. Carries the ZODB storage, db, and connection objects once 
    they are opened.

    Only one Project file can be open at one time.
    """
    def __init__(self):
        self.storage = None
        self.db = None
        self.connection = None
        self.root = None

    def load(self):
        """
        Returns a Project object stored in a ZODB file storage. File selected by 
        user in a graphical 'file dialog' interface.
        """
        tk_root = Tk()
        tk_root.withdraw()
        filepath = filedialog.askopenfilename(title="Please select a database file")

        self.storage = FileStorage(filepath)
        self.db = ZODB.DB(self.storage)
        self.connection = self.db.open()
        self.root = self.connection.root()
        project = copy.deepcopy(self.root["project"])
        self.connection.close()
        return project

    def save(self, project: Project):
        """
        Commits the project to the ZODB
        """
        if not hasattr(project, "filename"):
            project.filename = input("Enter a filename (without extension):")
        if not hasattr(project, "filepath"):
            tk_root = Tk()
            tk_root.withdraw()
            project.filepath = filedialog.askdirectory(title="Please select a directory")

        filename = project.filename
        full_path = project.filepath + "/" + filename

        if not (self.storage and self.db):
            self.storage=FileStorage(full_path)
            self.db=ZODB.DB(self.storage)
        else:
            self.connection=self.db.open()
            self.root=self.connection.root()

        self.root['project'] = project
        transaction.commit()
        self.connection.close()
person Connor Ferster    schedule 09.01.2019