Helps you create and manage your own rollbackable transactions.
$ pip install pysyphe
Tests should be run under python 2.7 and python 3.6 to tests everything
$ pip install tox
$ tox -e py27,py36
Coverage reports will be the merge of the coverage for py27 and py36.
Rollbackable Actions
Create actions and chain them in a pipeline:
>>> from pysyphe.actions import ActionsPipeline, Action
>>> def hello_world():
... print("Hello world!")
...
>>> def im_alive():
... print("I'm Alive!!!")
...
>>> action1 = Action(hello_world)
>>> action1.do()
Hello world!
>>> action2 = Action(im_alive)
>>> pipeline = ActionsPipeline([action1, action2])
>>> pipeline.do()
Hello world!
I'm Alive!!!
Create rollback for your actions:
>>> def hello_world():
... print("Hello world!")
...
>>> def goodbye_world():
... print("Goodbye world!")
...
>>> action = Action(hello_world, goodbye_world)
>>> action.do()
Hello world!
>>> action.undo()
Goodbye world!
Rollback pipelines:
>>> def hello_world():
... print("Hello world!")
...
>>> def goodbye_world():
... print("Goodbye world!")
...
>>> def im_alive():
... print("I'm Alive!!!")
...
>>> def im_dead():
... print("I'm Dead!!!")
...
>>> pipeline = ActionsPipeline([
... Action(im_alive, im_dead),
... Action(hello_world, goodbye_world)])
>>> pipeline.undo() # Nothing to rollback
>>> pipeline.do()
I'm Alive!!!
Hello world!
>>> pipeline.undo() # Will be done in reverse order.
Goodbye world!
I'm Dead!!!
Rollback only what have been done:
>>> def hello_world():
... print("Hello world!")
...
>>> def goodbye_world():
... print("Goodbye world!")
...
>>> def im_alive():
... print("I'm Alive!!!")
...
>>> def im_dead():
... print("I'm Dead!!!")
...
>>> def failure():
... raise Exception("I broke your pipeline, what are you gonna do?")
...
>>> pipeline = ActionsPipeline([
... Action(hello_world, goodbye_world),
... Action(failure, lambda: None), # rollback will be an empty function
... Action(im_alive, im_dead)])
>>> try:
... pipeline.do()
... except Exception:
... pipeline.undo()
...
Hello world!
Goodbye world!
Define actions with a state:
>>> from pysyphe.actions import statefull_action
>>> @statefull_action(["name"])
... def hello(state):
... print("Hello {}".format(state["name"]))
... state["name"] = "Dear " + state["name"]
...
>>> @hello.rollback_action()
... def goodbye(state):
... print("Goodbye {}".format(state["name"]))
...
>>> action = hello.get_prepared_action(name="reader") # It must be prepared for state to be inialised
>>> action.do()
Hello reader
>>> action.undo()
Goodbye Dear reader
>>> action.do()
Hello Dear reader
>>> action.do()
Hello Dear Dear reader
Chain actions with a state:
>>> @statefull_action(["name"])
... def hello(state):
... print("Hello {}".format(state["name"]))
... state["name"] = "Dear " + state["name"]
...
>>> @hello.rollback_action()
... def goodbye(state):
... print("Goodbye {}".format(state["name"]))
...
>>> action = hello.get_prepared_action(name="reader")
>>> action2 = hello.get_prepared_action(name=action.state.ref_to("name")) # We can access the state of a previous action but read only !
>>> action3 = hello.get_prepared_action(name=action2.state.ref_to("name"))
>>> pipeline = ActionsPipeline([action, action2, action3)])
>>> pipeline.do()
Hello reader
Hello Dear reader
Hello Dear Dear reader
>>> pipeline.undo()
Goodbye Dear Dear Dear reader
Goodbye Dear Dear reader
Goodbye Dear reader
>>> pipeline.do()
Hello Dear reader
Hello Dear Dear reader
Hello Dear Dear Dear reader
>>> pipeline.undo()
Goodbye Dear Dear Dear Dear reader
Goodbye Dear Dear Dear reader
Goodbye Dear Dear reader
Transactions
Create transaction handlers and manage them:
>>> from pysyphe.transactions import TransactionHandler, TransactionsManager
>>> class LoggingTransactionHandler(TransactionHandler):
... def __init__(self, name, will_fail):
... self.name = name
... self.will_fail = will_fail
... def begin(self):
... print("BEGIN {}!".format(self.name))
... def execute(self):
... if self.will_fail:
... raise Exception("Your transaction failed, what are you gonna do?")
... def commit(self):
... print("COMMIT {}!".format(self.name))
... def rollback(self):
... print("ROLLBACK {}!".format(self.name))
...
>>> tran_success = LoggingTransactionHandler("first", will_fail=False)
>>> tran_fail = LoggingTransactionHandler("second", will_fail=True)
>>> manager = TransactionsManager()
>>> manager.add_transaction_handler(tran_success)
>>> with manager.begin():
... manager.execute()
... manager.commit()
...
BEGIN first!
COMMIT first!
>>> manager = TransactionsManager()
>>> manager.add_transaction_handler(tran_success)
>>> manager.add_transaction_handler(tran_fail)
>>> with manager.begin(): # The transaction manager will rollback all transactions if an exception occurs.
... manager.execute()
... manager.commit()
...
BEGIN first!
BEGIN second!
ROLLBACK first!
ROLLBACK second!
Traceback (most recent call last):
File "<stdin>", line -, in <module>
File ".../pysyphe/transactions.py", line -, in execute
transaction_handler.execute()
File "<stdin>", line -, in execute
Exception: Your transaction failed, what are you gonna do?
- Generate the documentation
- Add a "How-To correctly write unit actions to get the most out of pysyphe" into the documentation