Transactions ============ Basic Concept ------------- Transactions represent blocks of commands that should be ran in sequence to one another and will continue to be ran if and only there are no failures, similar to the commands within a ``Makefile``. For example if the ``make`` command fails then the ``make install`` command wouldn't be ran:: from capris.transaction import transactional @transactional() def setup(transaction): make = transaction.make make.run() make.install.run() return transaction transaction = setup() results = transaction.execute() All runnables gained from the transaction that are ran will not be ran until the Transaction.execute method is called, and will return None instead of a response. This doesn't mean that they cannot be piped with other regular commands/runnables because the implementation doesn't care about run-returned responses. If you need to play around with the return values after they are ran, it is recommended that you use callback hooking on the iostream object, because the transactional runnables do not return a response when ran. They will just enqueue the command to be ran:: @transactional() def setup(transaction): make = transaction.make iostream = make.iostream def callback(res): pass iostream.run() return transaction Transaction API --------------- .. function:: capris.transaction.transactional(lazy=False) A decorator that accepts a single argument, the lazy option and returns a function that will call the decorated function with a Transaction object as the first parameter, for example:: @transactional() def setup(transaction): print(transaction) setup() # If you set the lazy option to True, the function will only be ran if the defined property of the Transaction object evaluates to False, and the wrapper generated by the decorator will return the transaction object:: context = [] @transactional(lazy=True) def setup(transaction): context.append(transaction) transaction.grep.run() return transaction res = [setup() for _ in range(2)] assert len(context) == 1 assert all(i is res[0] for i in res) If you use regular runnables and run them within the function decorated by ``@transactional``, they will run as normal due to the lack of sorcery and hackery. .. class:: capris.transaction.Transaction() .. method:: command(string) An alias would be to use the getattr magic, but you can just do a regular python call if you need dynamism and that's your thing. Returns a lazy TransactionCommand object that will not run when the run method is called. Example usage:: transaction.command('make') transaction.make Depending on your intents it is often better to use the getattr magic since it makes the code easier to read. .. method:: execute() Executes the transaction and runs all of the commands defined in the transaction, raising a RuntimeError exception if one of the commands failed. Returns a list of ``capris.core.Response`` objects returned by calling the correct (i.e. pipes -> Pipe.run, etc) runner method on the object. For example: .. code-block:: python results = transaction.execute() for response in results: # ... .. attribute:: commands A list of commands registered on the transaction object. You shouldn't manipulate this directly as the data stored in the list may change in format without prior notice. The current format is: .. code-block:: python (runnable, runner_method, args, kwargs) Essentially when you call the run method of runnables returned by the transaction.command method, you are registering a command to be ran on the transaction object. .. attribute:: defined A property that determines if a transaction is defined, or whether there are commands registered (their run method is called) on the transaction. For example: .. code-block:: python @transactional() def setup(transaction): if not transaction.defined: # continue defining return transaction