Introduction ============ Actions and dependencies ------------------------ In *ActionTree*, you create a dependency graph of actions to be executed and then call the :func:`.execute` function on its root. For example, let's say you want to generate three files, and then concatenate them into a fourth file. First, import :mod:`.ActionTree` .. BEGIN SECTION introduction_actions.py >>> from ActionTree import Action, execute Then create your specialized :class:`.Action` classes: >>> class CreateFile(Action): ... def __init__(self, name): ... super(CreateFile, self).__init__("create {}".format(name)) ... self.__name = name ... ... def do_execute(self, dependency_statuses): ... with open(self.__name, "w") as f: ... f.write("This is {}\n".format(self.__name)) >>> class ConcatFiles(Action): ... def __init__(self, files, name): ... super(ConcatFiles, self).__init__("concat") ... self.__files = files ... self.__name = name ... ... def do_execute(self, dependency_statuses): ... with open(self.__name, "w") as output: ... for file in self.__files: ... with open(file) as input: ... output.write(input.read()) .. END SECTION introduction_actions.py .. We have to import these classes to make them picklable in doctests .. doctest:: :hide: >>> from introduction_actions import CreateFile, ConcatFiles Create an actions dependency graph: >>> concat = ConcatFiles(["first", "second", "third"], "fourth") >>> concat.add_dependency(CreateFile("first")) >>> concat.add_dependency(CreateFile("second")) >>> concat.add_dependency(CreateFile("third")) And :func:`.execute` it: >>> execute(concat).is_success True The actions have been executed successfully: >>> def cat(name): ... with open(name) as f: ... print(f.read(), end="") >>> cat("first") This is first >>> cat("second") This is second >>> cat("third") This is third >>> cat("fourth") This is first This is second This is third You have no guaranty about the order of execution of the ``CreateFile`` actions, but you are sure that they are all finished before the ``ConcatFiles`` action starts. If your system has several CPUs, the ``CreateFile`` actions have been executed concurrently. Preview ------- If you just want to know what *would* be done, you can use :meth:`.Action.get_possible_execution_order`: >>> [a.label for a in concat.get_possible_execution_order()] ['create first', 'create second', 'create third', 'concat'] As said earlier, you have no guaranty about the order of the first three actions, so :meth:`~.Action.get_possible_execution_order` returns *one* possible order. Stock actions ------------- ActionTree ships with some :mod:`~.ActionTree.stock` actions for common tasks, including running subprocesses and basic operations on files and directories. Say you want to compile :ref:`two C++ files ` and link them: .. BEGIN SECTION stock_link.py >>> from ActionTree import execute >>> from ActionTree.stock import CreateDirectory, CallSubprocess >>> make_build_dir = CreateDirectory("_build") >>> compile_a = CallSubprocess(["g++", "-c", "a.cpp", "-o", "_build/a.o"], label="g++ -c a.cpp") >>> compile_a.add_dependency(make_build_dir) >>> compile_b = CallSubprocess(["g++", "-c", "b.cpp", "-o", "_build/b.o"], label="g++ -c b.cpp") >>> compile_b.add_dependency(make_build_dir) >>> link = CallSubprocess(["g++", "-o", "_build/test", "_build/a.o", "_build/b.o"], label="g++ -o test") >>> link.add_dependency(compile_a) >>> link.add_dependency(compile_b) .. END SECTION stock_link.py >>> link_report = execute(link)