File: execution.rst

package info (click to toggle)
python-invoke 0.11.1%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 1,136 kB
  • ctags: 1,702
  • sloc: python: 5,614; makefile: 37; sh: 36
file content (191 lines) | stat: -rw-r--r-- 4,778 bytes parent folder | download
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.