Hooks

Registering a method of your plugin as a hook means that this method will be called at a particular point during evariste execution.

Hook types

Method hooks

Method hooks are defined as decorator: they return a wrapped function, may (or may not) call the original function, and may (or may not) change the returned value.

Example of a method hook.
@methodhook("File.make_archive")
def make_archive(self, function):
    """Do something wile building archive"""

    @functools.wraps(function)
    def wrapped(tree, destdir):
        """Wrapped function."""
        # Do something before original function call.
        # Then call the original function.
        value = function(tree, destdir)
        # Do something after the original function call.
        # Maybe change the returnd value.
        return value

    return wrapped

Context hooks

Most of the time, you want to use a method hook, without the hassle of defining a wrapped function (because you won’t change the arguments or return value of the original function call). Any hook defined as a method hook can also be used as a context hook.

Your function must be a context manager (a contextlib.contextmanager() would make it even easier). Besides self, it is passed the arguments of the original function, and that original function is called between the __enter__() and __exit__() calls.

Example of a context hook
 @contexthook("Builder.compile")
 @contextlib.contextmanager
 def builder_compile(self, builder):
     # Do something before calling the original function

     # Call the original function
     yield

     # Do something after having called the original function

Iteration hooks

The last hook type are iteration hooks. Functions registered as such a hook must be iterators, and Évariste will aggregate all the item iterated by all functions registered as this hook.

Example of an iteration hook
@iterhook("Tree.prune_before")
def foo(self, tree):
    yield from self.bar(tree)

Chronological list of hooks

Here is the chronological list of hooks, that is, the list of the hooks, in the order in which they are called when running Évariste.

Just enable the debug.hooks plugin to print this list to standard output.

  1. An instance of the plugin is created: plugins.Plugin.__init__().

  2. Tree (Method hooks): for every file and directory in the repository, the Method hooks Tree is called (__enter__() and __exit__()).

  3. Builder.compile.__enter__() (Method hooks): About to build the tree.

  4. Tree.prune_before() (Iteration hooks): Methods must iterate files that will be pruned from the tree before file compilation (files and directories that won’t be compiled, and won’t appear in the final output). This method is called once for every file and directory of the tree. Argument: a Tree object.

  5. File.compile.__enter__() (Method hooks): About to compile the file. This method is called for every file in the repository. Note that file compilation is done in threads, so you don’t know in which order files will be compiled.

  6. File.compile.__exit__() (Method hooks): Done compiling the file. Same remarks as above.

  7. Tree.prune_after() (Iteration hooks): Methods must iterate files that will be pruned from the tree after file compilation (files and directories that may have been compiled, and won’t appear in the final output). This method is called once for every file and directory of the tree. Argument: a Tree object.

  8. File.make_archive.__enter__() (Method hooks): About to build the archive of the current file and its dependencies. This hook is called once for every file in the repository.

  9. File.make_archive.__exit__() (Method hooks): Done building the archive.

  10. Builder.compile.__exit__() (Method hooks): Done building the tree.

  11. Builder.close.__enter__() (Method hooks): About to close the builder. This method is only called if compilation was successful.

  12. Builder.close.__exit__() (Method hooks): About to close the builder. See remark above.

Create your own hooks

Method and Context hooks

Defining a new method hook is done using the hooks.setmethodhook(). The following example defines a context hook.

Definition of a context hook
from evariste.hooks import setmethodhook

class Foo:

    @setmethodhook()
    def bar(self, baz):
        bla_bla_bla()

Then, any plugin can register the method or context hook Foo.bar (class name dot method name, or class name only for the constructor) that will be called whenever method Foo.bar() is called.

Any method hook is also a context hook (and it is not possible to define a context hook that is not a method hook).

Iteration hooks

Iteration hooks are defined using the plugins.Loader.applyiterhook() function (the plugins.Loader instance being an attribute of the builder.Builder one).

For instance, if a plugin contains the foolowing lines:

Definition of an iteration hook
for item in self.shared.builder.plugins.applyiterhook("foo", bar):
    baz(bar)

Then, every method registered as an iteration hook foo will be called with the argument bar, and whatever they iterate will be iterated in the for loop in this example.