Поддержание потока кода с возможностью отката в Python без экстремальной идентификации - PullRequest
1 голос
/ 19 августа 2011

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

Лучшее подходящее решение, которое мне пришло в голову, эторабочий процесс python try-finally, но он довольно проблематичен, когда последовательность команд длинная:

try:
    # perform action
    try:
        # perform action
        try: 
            # ...
        finally:
            # unroll
    finally:
        # unroll
finally:
    # unroll

Таким образом, чем больше команд мне нужно, тем глубже мои отступы и вложенность и тем меньше мой код для чтения,Я рассмотрел некоторые другие решения, такие как поддержание стека, где для каждой команды я нажимаю действие отката, но это может быть довольно сложным, и мне не нравится вставлять связанные методы в стеки.Я также рассмотрел вопрос об увеличении счетчика для каждого действия, которое я выполняю, затем в отдельности окончательно определился с тем типом отката, который мне нужен, в зависимости от счетчика, но опять же, поддержка такого кода становится проблемой.

БольшинствоХиты, которые я получил при поиске «транзакций» и «отката», были связаны с БД и не очень хорошо подходили для более общего вида кода ... У кого-нибудь есть хорошая идея относительно того, как систематически сгладить это злодеяние?

1 Ответ

5 голосов
/ 19 августа 2011

Разве объекты диспетчера контекста и оператор с не улучшат ситуацию?Особенно, если вы можете использовать версию Python, где оператор with поддерживает несколько выражений контекста, например 2.7 или 3.x.Вот пример:

class Action(object):
    def __init__(self, count):
        self.count = count
    def perform(self):
        print "perform " + str(self.count)
        if self.count == 2:
            raise Exception("self.count is " + str(self.count))
    def commit(self):
        print "commit " + str(self.count)
    def rollback(self):
        print "rollback " + str(self.count)
    def __enter__(self):
        perform()
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_value is None:
            self.commit()
        else:
            self.rollback()

with Action(1), Action(2), Action(3):
    pass

Вам необходимо переместить свой код в набор «транзакционных» классов, таких как Action выше, где выполняемое действие выполняется в __enter__()метода и, если это нормально завершится, вам будет гарантировано, что будет вызван соответствующий __exit()__ метод.

Обратите внимание, что мой пример не совсем соответствует вашему;вам нужно настроить, что выполнять в методах __enter__(), а что выполнять в теле оператора with.В этом случае вы можете использовать следующий синтаксис:

with Action(1) as a1, Action(2) as a2:
    pass

Чтобы иметь возможность доступа к объектам Action из тела оператора with.

...