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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
===============
Getting started
===============
This document presents a whirlwind tour of Invoke's feature set. Please see the
links throughout for detailed conceptual & API docs. For installation help, see
the project's `installation page <http://www.pyinvoke.org/installing.html>`_.
.. _defining-and-running-task-functions:
Defining and running task functions
===================================
The core use case for Invoke is setting up a collection of task functions and
executing them. This is pretty easy -- all you need is to make a file called
``tasks.py`` importing the `.task` decorator and decorating one or more
functions. You will also need to add an arbitrarily-named context argument
(convention is to use ``c``, ``ctx`` or ``context``) as the first positional
arg. Don't worry about using this context parameter yet.
Let's start with a dummy Sphinx docs building task::
from invoke import task
@task
def build(c):
print("Building!")
You can then execute that new task by telling Invoke's command line runner,
``invoke``, that you want it to run::
$ invoke build
Building!
The function body can be any Python you want -- anything at all.
Task parameters
===============
Functions can have arguments, and thus so can tasks. By default, your task
functions' args/kwargs are mapped automatically to both long and short CLI
flags, as per :ref:`the CLI docs <task-arguments>`. For example, if we add a
``clean`` argument and give it a boolean default, it will show up as a set of
toggle flags, ``--clean`` and ``-c``::
@task
def build(c, clean=False):
if clean:
print("Cleaning!")
print("Building!")
Invocations::
$ invoke build -c
$ invoke build --clean
Naturally, other default argument values will allow giving string or integer
values. Arguments with no default values are assumed to take strings, and can
also be given as positional arguments. Take this incredibly contrived snippet
for example::
@task
def hi(c, name):
print("Hi {}!".format(name))
It can be invoked in the following ways, all resulting in "Hi Name!"::
$ invoke hi Name
$ invoke hi --name Name
$ invoke hi --name=Name
$ invoke hi -n Name
$ invoke hi -nName
Adding metadata via `@task <.task>`
-----------------------------------
`@task <.task>` can be used without any arguments, as above, but it's also a
convenient vector for additional metadata about the task function it decorates.
One common example is describing the task's arguments, via the ``help``
parameter (in addition to optionally giving task-level help via the
docstring)::
@task(help={'name': "Name of the person to say hi to."})
def hi(c, name):
"""
Say hi to someone.
"""
print("Hi {}!".format(name))
This description will show up when invoking ``--help``::
$ invoke --help hi
Usage: inv[oke] [--core-opts] hi [--options] [other tasks here ...]
Docstring:
Say hi to someone.
Options:
-n STRING, --name=STRING Name of the person to say hi to.
More details on task parameterization and metadata can be found in
:doc:`/concepts/invoking-tasks` (for the command-line & parsing side of things)
and in the `.task` API documentation (for the declaration side).
Listing tasks
=============
You'll sometimes want to see what tasks are available in a given
``tasks.py`` -- ``invoke`` can be told to list them instead of executing
something::
$ invoke --list
Available tasks:
build
This will also print the first line of each task’s docstring, if it has one. To
see what else is available besides ``--list``, say ``invoke --help``.
Running shell commands
======================
Many use cases for Invoke involve running local shell commands, similar to
programs like Make or Rake. This is done via the `~.Context.run` function::
from invoke import task
@task
def build(c):
c.run("sphinx-build docs docs/_build")
You'll see the command's output in your terminal as it runs::
$ invoke build
Running Sphinx v1.1.3
loading pickled environment... done
...
build succeeded, 2 warnings.
`~.Context.run` has a number of arguments controlling its behavior, such as
activation of pseudo-terminals for complex programs requiring them, suppression
of exit-on-error behavior, hiding of subprocess' output (while still capturing
it for later review), and more. See `its API docs <.Context.run>` for details.
`~.Context.run` always returns a useful `.Result` object providing access to
the captured output, exit code, and other information.
.. _why-context:
Aside: what exactly is this 'context' arg anyway?
-------------------------------------------------
A common problem task runners face is transmission of "global" data - values
loaded from :doc:`configuration files </concepts/configuration>` or :ref:`other
configuration vectors <collection-configuration>`, given via CLI flags,
generated in 'setup' tasks, etc.
Some libraries (such as `Fabric <http://fabfile.org>`_ 1.x) implement this via
module-level attributes, which makes testing difficult and error prone, limits
concurrency, and increases implementation complexity.
Invoke encapsulates state in explicit `~.Context` objects, handed to tasks when
they execute . The context is the primary API endpoint, offering methods which
honor the current state (such as `.Context.run`) as well as access to that
state itself.
Declaring pre-tasks
===================
Tasks may be configured in a number of ways via the `.task` decorator. One of
these is to select one or more other tasks you wish to always run prior to
execution of your task, indicated by name.
Let's expand our docs builder with a new cleanup task that runs before every
build (but which, of course, can still be executed on its own)::
from invoke import task
@task
def clean(c):
c.run("rm -rf docs/_build")
@task(clean)
def build(c):
c.run("sphinx-build docs docs/_build")
Now when you ``invoke build``, it will automatically run ``clean`` first.
.. note::
If you're not a fan of the implicit "positional arguments are pre-run task
names" API, you can instead explicitly give the ``pre`` kwarg:
``@task(pre=[clean])``.
Details can be found in :ref:`how-tasks-run`.
Creating namespaces
===================
Right now, our ``tasks.py`` is implicitly for documentation only, but maybe our
project needs other non-doc things, like packaging/deploying, testing, etc. At
that point, a single flat namespace isn't enough, so Invoke lets you easily
build a :doc:`nested namespace <concepts/namespaces>`. Here's a quick example.
Let's first rename our ``tasks.py`` to be ``docs.py``; no other changes are
needed there. Then we create a new ``tasks.py``, and for the sake of brevity
populate it with a new, truly top level task called ``deploy``.
Finally, we can use a new API member, the `.Collection` class, to bind this
task and the ``docs`` module into a single explicit namespace. When Invoke
loads your task module, if a `.Collection` object bound as ``ns`` or
``namespace`` exists it will get used for the root namespace::
from invoke import Collection, task
import docs
@task
def deploy(c):
c.run("python setup.py sdist")
c.run("twine upload dist/*")
namespace = Collection(docs, deploy)
The result::
$ invoke --list
Available tasks:
deploy
docs.build
docs.clean
For a more detailed breakdown of how namespacing works, please see :doc:`the
docs <concepts/namespaces>`.
|