add transaction.py
This commit is contained in:
parent
4ceaef7522
commit
dc85629865
2 changed files with 657 additions and 0 deletions
203
static/files/transaction.py
Normal file
203
static/files/transaction.py
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
"""
|
||||
Transactions of attributes by inheriting the Transaction class
|
||||
|
||||
Basic Usage:
|
||||
|
||||
class Test(Transaction):
|
||||
pass
|
||||
|
||||
a = Test()
|
||||
a.test = "old state"
|
||||
a.commit()
|
||||
a.test = "bad state, roll me back"
|
||||
a.rollback()
|
||||
assert(a.test == "old state")
|
||||
|
||||
See also: https://harald.hoyer.xyz/2015/10/13/a-python-transaction-class/
|
||||
|
||||
Copyright (C) 2008 Harald Hoyer <harald@redhat.com>
|
||||
Copyright (C) 2008 Red Hat, Inc.
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
|
||||
def _checksetseen(what, seen):
|
||||
"checks and sets the obj id in seen"
|
||||
if what in seen:
|
||||
return True
|
||||
seen.add(what)
|
||||
return False
|
||||
|
||||
|
||||
class Transaction(object):
|
||||
"""
|
||||
This class allows sub-classes to commit changes to an instance to a
|
||||
history, and rollback to previous states.
|
||||
|
||||
Because the class only stores attributes in self.__dict__ sub-classes
|
||||
need to use the methods __getstate__ and __setstate__ to provide additional
|
||||
state information. See the Transactionlist below for an example usage.
|
||||
"""
|
||||
|
||||
def commit(self, **kwargs):
|
||||
"""
|
||||
Commit the object state.
|
||||
|
||||
If the optional argument "deep" is set to False,
|
||||
objects of class Transaction stored in this object will
|
||||
not be committed.
|
||||
"""
|
||||
seen = kwargs.get("_commit_seen", set())
|
||||
if _checksetseen(id(self), seen):
|
||||
return
|
||||
deep = kwargs.get("deep", True)
|
||||
|
||||
# Do not deepcopy the Transaction objects. We want to keep the
|
||||
# reference. Instead commit() them.
|
||||
state = dict()
|
||||
for key, val in self.__dict__.items():
|
||||
if isinstance(val, Transaction):
|
||||
state[key] = val
|
||||
if deep:
|
||||
val.commit(_commit_seen=seen)
|
||||
elif key == "__l":
|
||||
# do not deepcopy our old state
|
||||
state[key] = val
|
||||
else:
|
||||
state[key] = copy.deepcopy(val)
|
||||
|
||||
if hasattr(self, "__getstate__"):
|
||||
state = (state, getattr(self, "__getstate__")())
|
||||
|
||||
self.__dict__["__l"] = state
|
||||
|
||||
def rollback(self, **kwargs):
|
||||
"""
|
||||
Rollback the last committed object state.
|
||||
|
||||
If the optional argument "deep" is set to False,
|
||||
objects of class Transaction stored in this object will
|
||||
not be rolled back.
|
||||
"""
|
||||
seen = kwargs.get("_rollback_seen", set())
|
||||
if _checksetseen(id(self), seen):
|
||||
return
|
||||
|
||||
deep = kwargs.get("deep", True)
|
||||
state = None
|
||||
extrastate = None
|
||||
gotstate = False
|
||||
gotextrastate = False
|
||||
if "__l" in self.__dict__:
|
||||
state = self.__dict__["__l"]
|
||||
gotstate = True
|
||||
if type(state) is tuple:
|
||||
gotextrastate = True
|
||||
(state, extrastate) = state
|
||||
|
||||
# rollback our childs, then ourselves
|
||||
for child in self.__dict__.values():
|
||||
if isinstance(child, Transaction):
|
||||
if deep:
|
||||
child.rollback(_rollback_seen=seen)
|
||||
|
||||
if gotstate:
|
||||
self.__dict__.clear()
|
||||
self.__dict__.update(state)
|
||||
|
||||
if gotextrastate and hasattr(self, "__setstate__"):
|
||||
getattr(self, "__setstate__")(extrastate)
|
||||
|
||||
|
||||
class Transactionlist(list, Transaction):
|
||||
"""
|
||||
An example subclass of list, which inherits transactions.
|
||||
|
||||
Due to the special list implementation, we need the
|
||||
__getstate__ and __setstate__ methods.
|
||||
|
||||
See the code for the implementation.
|
||||
"""
|
||||
|
||||
def commit(self, **kwargs):
|
||||
"""
|
||||
Commit the object state.
|
||||
|
||||
If the optional argument "deep" is set to False,
|
||||
objects of class Transaction stored in this object will
|
||||
not be committed.
|
||||
"""
|
||||
# make a local copy of the recursive marker
|
||||
seen = set(kwargs.get("_commit_seen", set()))
|
||||
|
||||
super(Transactionlist, self).commit(**kwargs)
|
||||
|
||||
if _checksetseen(id(self), seen):
|
||||
return
|
||||
|
||||
deep = kwargs.get("deep", True)
|
||||
if deep:
|
||||
for val in self:
|
||||
if isinstance(val, Transaction):
|
||||
val.commit()
|
||||
|
||||
def rollback(self, **kwargs):
|
||||
"""
|
||||
Rollback the last committed object state.
|
||||
|
||||
If the optional argument "deep" is set to False,
|
||||
objects of class Transaction stored in this object will
|
||||
not be rolled back.
|
||||
"""
|
||||
# make a local copy of the recursive marker
|
||||
seen = set(kwargs.get("_rollback_seen", set()))
|
||||
|
||||
super(Transactionlist, self).rollback(**kwargs)
|
||||
|
||||
if _checksetseen(id(self), seen):
|
||||
return
|
||||
|
||||
deep = kwargs.get("deep", True)
|
||||
if deep:
|
||||
for val in self:
|
||||
if isinstance(val, Transaction):
|
||||
val.rollback()
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
return a deepcopy of all non Transaction class objects in our list,
|
||||
and a reference for the committed Transaction objects.
|
||||
|
||||
"""
|
||||
state = []
|
||||
for val in self:
|
||||
if isinstance(val, Transaction):
|
||||
state.append(val)
|
||||
else:
|
||||
state.append(copy.deepcopy(val))
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(self, state):
|
||||
"clear the list and restore all objects from the state"
|
||||
del self[:]
|
||||
self.extend(state)
|
||||
|
||||
|
||||
__author__ = "Harald Hoyer <harald@redhat.com>"
|
||||
Loading…
Add table
Add a link
Reference in a new issue