=======================
Constructing namespaces
=======================

The :doc:`base case </getting_started>` of loading a single module of tasks
works fine initially, but advanced users typically need more organization, such
as separating tasks into a tree of nested namespaces.

The `.Collection` class provides an API for organizing tasks (and :ref:`their
configuration <collection-configuration>`) into a tree-like structure. When
referenced by strings (e.g. on the CLI or in pre/post hooks) tasks in nested
namespaces use a dot-separated syntax, e.g. ``docs.build``.

In this section, we show how building namespaces with this API is flexible but
also allows following Python package layouts with minimal boilerplate.

Starting out
============

One unnamed ``Collection`` is always the namespace root; in the implicit base
case, Invoke creates one for you from the tasks in your tasks module.  Create
your own, named ``namespace`` or ``ns``, to set up an explicit namespace (i.e.
to skip the default "pull in all Task objects" behavior)::

    from invoke import Collection

    ns = Collection()

Add tasks with `.Collection.add_task`. `~.Collection.add_task` can take an
`.Task` object, such as those generated by the `.task` decorator::

    from invoke import Collection, task, run

    @task
    def release():
        run("python setup.py sdist register upload")

    ns = Collection()
    ns.add_task(release)

Our available tasks list now looks like this::

    $ invoke --list
    Available tasks:

        release

Naming your tasks
=================

By default, a task's function name is used as its namespace identifier, but you
may override this by giving a ``name`` argument to either `@task <.task>` (i.e.
at definition time) or `.Collection.add_task` (i.e. at binding/attachment
time).

For example, say you have a variable name collision in your tasks module --
perhaps you want to expose a ``dir`` task, which shadows a Python builtin.
Naming your function itself ``dir`` is a bad idea, but you can name the
function something like ``dir_`` and then tell ``@task`` the "real" name::

    @task(name='dir')
    def dir_():
        # ...

On the other side, you might have obtained a task object that doesn't fit with
the names you want in your namespace, and can rename it at attachment time.
Maybe we want to rename our ``release`` task to be called ``deploy`` instead::

    ns = Collection()
    ns.add_task(release, name='deploy')

The result::

    $ invoke --list
    Available tasks:

        deploy

.. note::
    The ``name`` kwarg is the 2nd argument to `~.Collection.add_task`, so those
    in a hurry can simply say::

        ns.add_task(release, 'deploy')


Aliases
-------

.. FIXME: add back aliases and merge at add_task time, as we do with name. HURR

Tasks may have additional names or aliases, given as the ``aliases`` keyword
argument; these are appended to, instead of replacing, any implicit or explicit
``name`` value::

    ns.add_task(release, aliases=('deploy', 'pypi'))

Result, with three names for the same task::

    $ invoke --list
    Available tasks:

        release
        deploy
        pypi

.. note::
    The convenience decorator `@task <.task>` is another method of
    setting aliases (e.g. ``@task(aliases=('foo', 'bar'))``, and is useful for
    ensuring a given task always has some aliases set no matter how it's added
    to a namespace.
        
Nesting collections
===================

The point of namespacing is to have sub-namespaces; to do this in Invoke,
create additional `.Collection` instances and add them to their parent
collection via `.Collection.add_collection`. For example, let's say we have a
couple of documentation tasks::

    @task
    def build_docs():
        run("sphinx-build docs docs/_build")

    @task
    def clean_docs():
        run("rm -rf docs/_build")

We can bundle them up into a new, named collection like so::

    docs = Collection('docs')
    docs.add_task(build_docs, 'build')
    docs.add_task(clean_docs, 'clean')

And then add this new collection under the root namespace with
``add_collection``::

    ns.add_collection(docs)

The result (assuming for now that ``ns`` currently just contains the original
``release`` task)::

    $ invoke --list
    Available tasks:

        release
        docs.build
        docs.clean

As with tasks, collections may be explicitly bound to their parents with a
different name than they were originally given (if any) via a ``name`` kwarg
(also, as with ``add_task``, the 2nd regular arg)::

    ns.add_collection(docs, 'sphinx')

Result::

    $ invoke --list
    Available tasks:

        release
        sphinx.build
        sphinx.clean

Importing modules as collections
================================

A simple tactic which Invoke itself uses in the trivial, single-module
case is to use `.Collection.from_module` -- a classmethod
serving as an alternate ``Collection`` constructor which takes a Python module
object as its first argument.

Modules given to this method are scanned for ``Task`` instances, which are
added to a new ``Collection``. By default, this collection's name is taken from
the module name (the ``__name__`` attribute), though it can also be supplied
explicitly.

.. note::
    As with the default task module, you can override this default loading
    behavior by declaring a ``ns`` or ``namespace`` `.Collection` object at top
    level in the loaded module.

For example, let's reorganize our earlier single-file example into a Python
package with several submodules. First, ``tasks/release.py``::

    from invoke import task, run

    @task
    def release():
        run("python setup.py sdist register upload")

And ``tasks/docs.py``::

    from invoke import task, run

    @task
    def build():
        run("sphinx-build docs docs/_build")

    @task
    def clean():
        run("rm -rf docs/_build")

Tying them together is ``tasks/__init__.py``::

    from invoke import Collection

    import release, docs

    ns = Collection()
    ns.add_collection(Collection.from_module(release))
    ns.add_collection(Collection.from_module(docs))

This form of the API is a little unwieldy in practice. Thankfully there's a
shortcut: ``add_collection`` will notice when handed a module object as its
first argument and call ``Collection.from_module`` for you internally::

    ns = Collection()
    ns.add_collection(release)
    ns.add_collection(docs)

Either way, the result::

    $ invoke --list
    Available tasks:

        release.release
        docs.build
        docs.clean


Default tasks
=============

Tasks may be declared as the default task to invoke for the collection they
belong to, e.g. by giving ``default=True`` to `@task <.task>` (or to
`.Collection.add_task`.) This is useful when you have a bunch of related tasks
in a namespace but one of them is the most commonly used, and maps well to the
namespace as a whole.

For example, in the documentation submodule we've been experimenting with so
far, the ``build`` task makes sense as a default, so we can say things like
``invoke docs`` as a shortcut to ``invoke docs.build``. This is easy to do::

    @task(default=True)
    def build():
        # ...

When imported into the root namespace (as shown above) this alters the output
of ``--list``, highlighting the fact that ``docs.build`` can be invoked as
``docs`` if desired::

    $ invoke --list
    Available tasks:

        release.release
        docs.build (docs)
        docs.clean


Mix and match
=============

You're not limited to the specific tactics shown above -- now that you know
the basic tools of ``add_task`` and ``add_collection``, use whatever approach
best fits your needs.

For example, let's say you wanted to keep things organized into submodules, but
wanted to "promote" ``release.release`` back to the top level for convenience's
sake. Just because it's stored in a module doesn't mean we must use
``add_collection`` -- simply import the task itself and use ``add_task``
directly::

    from invoke import Collection

    import docs
    from release import release

    ns = Collection()
    ns.add_collection(docs)
    ns.add_task(release)

Result::

    $ invoke --list
    Available tasks:

        release
        docs.build
        docs.clean

More shortcuts
==============

Finally, you can even skip ``add_collection`` and ``add_task`` if your needs
are simple enough -- `.Collection`'s constructor will take
unknown arguments and build the namespace from their values as
appropriate::

    from invoke import Collection

    import docs, release

    ns = Collection(release.release, docs)

Notice how we gave both a task object (``release.release``) and a module
containing tasks (``docs``). The result is identical to the above::

    $ invoke --list
    Available tasks:

        release
        docs.build
        docs.clean

If given as keyword arguments, the keywords act like the ``name`` arguments do
in the ``add_*`` methods. Naturally, both can be mixed together as well::

    ns = Collection(docs, deploy=release.release)

Result::

    $ invoke --list
    Available tasks:

        deploy
        docs.build
        docs.clean

.. note::
    You can still name these ``Collection`` objects with a leading string
    argument if desired, which can be handy when building sub-collections.
