1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
==============
Task execution
==============
Invoke's task execution mechanisms attempt to bridge the needs of the simple,
base case and advanced use cases involving parameterization, pre/post call
hooks, and so forth. This document lays out the classes and methods involved,
walking you through a couple different scenarios.
Note that this is a generalized walkthrough -- this behavior is the same
regardless of whether the tasks were invoked via the command line, or via
library calls.
Base case
=========
In the simplest case, a task with no pre- or post-tasks runs one time. Example::
@task
def hello():
print("Hello, world!")
Execution::
$ invoke hello
Hello, world!
.. _pre-post-tasks:
Pre- and post-tasks
===================
Tasks that should always have another task executed before or after them, may
use the ``@task`` deocator's ``pre`` and/or ``post`` kwargs, like so::
@task
def clean():
print("Cleaning")
@task
def publish():
print("Publishing")
@task(pre=[clean], post=[publish])
def build():
print("Building")
Execution::
$ invoke build
Cleaning
Building
Publishing
These keyword arguments always take iterables. As a convenience, pre-tasks (and
pre-tasks only) may be given as positional arguments, in a manner similar to
build systems like ``make``. E.g. we could present part of the above example
as::
@task
def clean():
print("Cleaning")
@task(clean)
def build():
print("Building")
As before, ``invoke build`` would cause ``clean`` to run, then ``build``.
Recursive/chained pre/post-tasks
--------------------------------
Pre-tasks of pre-tasks will also be invoked (as will post-tasks of pre-tasks,
pre-tasks of post-tasks, etc) in a depth-first manner, recursively. Here's a
more complex (if slightly contrived) tasks file::
@task
def clean_html():
print("Cleaning HTML")
@task
def clean_tgz():
print("Cleaning .tar.gz files")
@task(clean_html, clean_tgz)
def clean():
print("Cleaned everything")
@task
def makedirs():
print("Making directories")
@task(clean, makedirs)
def build():
print("Building")
@task(build)
def deploy():
print("Deploying")
With a depth-first behavior, the below is hopefully intuitive to most users::
$ inv deploy
Cleaning HTML
Cleaning .tar.gz files
Cleaned everything
Making directories
Building
Deploying
Parameterizing pre/post-tasks
-----------------------------
By default, pre- and post-tasks are executed with no arguments, even if the
task triggering their execution was given some. When this is not suitable, you
can wrap the task objects with `~.tasks.call` objects which allow you to
specify a call signature::
@task
def clean(which=None):
which = which or 'pyc'
print("Cleaning {0}".format(which))
@task(pre=[call(clean, which='all')]) # or call(clean, 'all')
def build():
print("Building")
Example output::
$ invoke build
Cleaning all
Building
.. _deduping:
Task deduplication
==================
By default, any task that would run more than once during a session (due e.g.
to inclusion in pre/post tasks), will only be run once. Example task file::
@task
def clean():
print("Cleaning")
@task(clean)
def build():
print("Building")
@task(build)
def package():
print("Packaging")
With deduplication turned off (see below), the above would execute ``clean`` ->
``build`` -> ``build`` again -> ``package``. With duplication, the double
``build`` does not occur::
$ invoke build package
Cleaning
Building
Packaging
.. note::
Parameterized pre-tasks (using `~.tasks.call`) are deduped based on their
argument lists. For example, if ``clean`` was parameterized and hooked up
as a pre-task in two different ways - e.g. ``call(clean, 'html')`` and
``call(clean, 'all')`` - they would not get deduped should both end up
running in the same session.
However, two separate references to ``call(clean, 'html')`` *would* become
deduplicated.
Disabling deduplication
-----------------------
If you prefer your tasks to run every time no matter what, you can give the
``--no-dedupe`` core CLI option at runtime, or set the ``tasks.dedupe``
:doc:`config setting </concepts/configuration>` to ``False``. While it
doesn't make a ton of real-world sense, let's imagine we wanted to apply
``--no-dedupe`` to the above example; we'd see the following output::
$ invoke --no-dedupe build package
Cleaning
Building
Building
Packaging
The build step is now running twice.
|