Navigation

As of Fabric 1.1, there are two distinct methods you may use in order to define
which objects in your fabfile show up as tasks:

The “new” method starting in 1.1 considers instances of Task
or its subclasses, and also descends into imported modules to allow building
nested namespaces.

The “classic” method from 1.0 and earlier considers all public callable
objects (functions, classes etc) and only considers the objects in the
fabfile itself with no recursing into imported module.

Note

These two methods are mutually exclusive: if Fabric finds any
new-style task objects in your fabfile or in modules it imports, it will
assume you’ve committed to this method of task declaration and won’t
consider any non-Task callables. If no new-style tasks
are found, it reverts to the classic behavior.

The rest of this document explores these two methods in detail.

Note

To see exactly what tasks in your fabfile may be executed via fab, use
fab --list.

Fabric 1.1 introduced the Task class to facilitate new features
and enable some programming best practices, specifically:

Object-oriented tasks. Inheritance and all that comes with it can make
for much more sensible code reuse than passing around simple function
objects. The classic style of task declaration didn’t entirely rule this
out, but it also didn’t make it terribly easy.

Namespaces. Having an explicit method of declaring tasks makes it easier
to set up recursive namespaces without e.g. polluting your task list with the
contents of Python’s os module (which would show up as valid “tasks”
under the classic methodology.)

With the introduction of Task, there are two ways to set up new
tasks:

Decorate a regular module level function with @task, which transparently wraps the function in a
Task subclass. The function name will be used as the task
name when invoking.

Subclass Task (Task itself is intended to be
abstract), define a run method, and instantiate your subclass at module
level. Instances’ name attributes are used as the task name; if omitted
the instance’s variable name will be used instead.

@task may also be called with arguments to
customize its behavior. Any arguments not documented below are passed into the
constructor of the task_class being used, with the function itself as the
first argument (see Using custom subclasses with @task for details.)

task_class: The Task subclass used to wrap the decorated
function. Defaults to WrappedCallableTask.

aliases: An iterable of string names which will be used as aliases for
the wrapped function. See Aliases for details.

alias: Like aliases but taking a single string argument instead of an
iterable. If both alias and aliases are specified, aliases will
take precedence.

default: A boolean value determining whether the decorated task also
stands in for its containing module as a task name. See Default tasks.

In a similar manner to aliases, it’s sometimes useful to
designate a given task within a module as the “default” task, which may be
called by referencing just the module name. This can save typing and/or
allow for neater organization when there’s a single “main” task and a number
of related tasks or subroutines.

For example, a deploy submodule might contain tasks for provisioning new
servers, pushing code, migrating databases, and so forth – but it’d be very
convenient to highlight a task as the default “just deploy” action. Such a
deploy.py module might look like this:

Using @task(default=True) in the top level fabfile will cause the denoted
task to execute when a user invokes fab without any task names (similar to
e.g. make.) When using this shortcut, it is not possible to specify
arguments to the task itself – use a regular invocation of the task if this
is necessary.

If you’re used to classic-style tasks, an easy way to
think about Task subclasses is that their run method is
directly equivalent to a classic task; its arguments are the task arguments
(other than self) and its body is what gets executed.

Note how we had to instantiate an instance of our class; that’s simply normal
Python object-oriented programming at work. While it’s a small bit of
boilerplate right now – for example, Fabric doesn’t care about the name you
give the instantiation, only the instance’s name attribute – it’s well
worth the benefit of having the power of classes available.

We plan to extend the API in the future to make this experience a bit smoother.

It’s possible to marry custom Task subclasses with @task. This may be useful in cases where your core
execution logic doesn’t do anything class/object-specific, but you want to
take advantage of class metaprogramming or similar techniques.

Specifically, any Task subclass which is designed to take in a
callable as its first constructor argument (as the built-in
WrappedCallableTask does) may be specified as the
task_class argument to @task.

Fabric will automatically instantiate a copy of the given class, passing in
the wrapped function as the first argument. All other args/kwargs given to the
decorator (besides the “special” arguments documented in
Arguments) are added afterwards.

With classic tasks, fabfiles were limited to a single,
flat set of task names with no real way to organize them. In Fabric 1.1 and
newer, if you declare tasks the new way (via @task
or your own Task subclass instances) you may take advantage
of namespacing:

Any module objects imported into your fabfile will be recursed into, looking
for additional task objects.

Within submodules, you may control which objects are “exported” by using the
standard Python __all__ module-level variable name (thought they should
still be valid new-style task objects.)

These tasks will be given new dotted-notation names based on the modules they
came from, similar to Python’s own import syntax.

Let’s build up a fabfile package from simple to complex and see how this works.

As mentioned above, Fabric will examine any imported module objects for tasks,
regardless of where that module exists on your Python import path. For now we
just want to include our own, “nearby” tasks, so we’ll make a new submodule in
our package for dealing with, say, load balancers – lb.py:

@taskdefadd_backend():...

And we’ll add this to the top of __init__.py:

importlb

Now fab--list shows us:

deploycompresslb.add_backend

Again, with only one task in its own submodule, it looks kind of silly, but the
benefits should be pretty obvious.

Namespacing isn’t limited to just one level. Let’s say we had a larger setup
and wanted a namespace for database related tasks, with additional
differentiation inside that. We make a sub-package named db/ and inside it,
a migrations.py module:

@taskdeflist():...@taskdefrun():...

We need to make sure that this module is visible to anybody importing db,
so we add it to the sub-package’s __init__.py:

importmigrations

As a final step, we import the sub-package into our root-level __init__.py,
so now its first few lines look like this:

You may limit what Fabric “sees” when it examines imported modules, by using
the Python convention of a module level __all__ variable (a list of
variable names.) If we didn’t want the db.migrations.run task to show up by
default for some reason, we could add this to the top of db/migrations.py:

__all__=['list']

Note the lack of 'run' there. You could, if needed, import run directly
into some other part of the hierarchy, but otherwise it’ll remain hidden.

We’ve been keeping our fabfile package neatly organized and importing it in a
straightforward manner, but the filesystem layout doesn’t actually matter here.
All Fabric’s loader cares about is the names the modules are given when they’re
imported.

For example, if we changed the top of our root __init__.py to look like
this:

As a final note, we’ve been using the default Fabric --list
output during this section – it makes it more obvious what the actual task
names are. However, you can get a more nested or tree-like view by passing
nested to the --list-format option:

Python’s import statement effectively includes the imported objects in your
module’s namespace. Since Fabric’s fabfiles are just Python modules, this means
that imports are also considered as possible classic-style tasks, alongside
anything defined in the fabfile itself.

Note

This only applies to imported callable objects – not modules.
Imported modules only come into play if they contain new-style
tasks, at which point this section no longer
applies.

Because of this, we strongly recommend that you use the importmodule form
of importing, followed by module.callable(), which will result in a cleaner
fabfile API than doing frommoduleimportcallable.

For example, here’s a sample fabfile which uses urllib.urlopen to get some
data out of a webservice:

Our fabfile of only one task is showing two “tasks”, which is bad enough, and
an unsuspecting user might accidentally try to call faburlopen, which
probably won’t work very well. Imagine any real-world fabfile, which is likely
to be much more complex, and hopefully you can see how this could get messy
fast.