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

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()
# <capris.transaction.Transaction object at 0x...>

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
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.

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:

results = transaction.execute()
for response in results:
    # ...
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:

(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.

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:

@transactional()
def setup(transaction):
    if not transaction.defined:
        # continue defining
    return transaction

Capris

Navigation