#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""http://code.activestate.com/recipes/413838-memento-closure/"""
from copy import copy, deepcopy
def memento(obj, deep=False):
state = copy(obj.__dict__) if deep else deepcopy(obj.__dict__)
def restore():
obj.__dict__.clear()
obj.__dict__.update(state)
return restore
class Transaction:
"""A transaction guard.
This is, in fact, just syntactic sugar around a memento closure.
"""
deep = False
states = []
def __init__(self, *targets):
self.targets = targets
self.commit()
def commit(self):
self.states = [memento(target, self.deep) for target in self.targets]
def rollback(self):
for a_state in self.states:
a_state()
class Transactional(object):
"""Adds transactional semantics to methods. Methods decorated with
@Transactional will rollback to entry-state upon exceptions.
"""
def __init__(self, method):
self.method = method
def __get__(self, obj, T):
def transaction(*args, **kwargs):
state = memento(obj)
try:
return self.method(obj, *args, **kwargs)
except Exception as e:
state()
raise e
return transaction
class NumObj(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return '' % (self.__class__.__name__, self.value)
def increment(self):
self.value += 1
@Transactional
def do_stuff(self):
self.value = '1111' # doing stuff failed!
# Traceback (most recent call last):
# File "memento.py", line 97, in
# num_obj.do_stuff()
# File "memento.py", line 52, in transaction
# raise e
# File "memento.py", line 49, in transaction
# return self.method(obj, *args, **kwargs)
# File "memento.py", line 70, in do_stuff
# self.increment() #