"""
Auto-generate Fabric tasks for :term:`environments<environment>`.

The :func:`generate_tasks` function creates Fabric tasks to set the current
environment definition and is intended to be used as a setup step to other
standard Confab tasks (e.g. pull) to customize configuration data::

    fab dev:web,queue push

"""
from fabric.api import abort, env
from fabric.state import commands

from confab.definitions import Settings


def _add_task(name, task, doc):
    """
    Register a Fabric task.
    """
    task_wrapper = lambda *args: task(*args)
    setattr(task_wrapper, '__doc__', doc)
    commands[name] = task_wrapper


def generate_tasks(settings_path=None):
    """
    Autogenerate `env` tasks for all defined environments.
    """
    def create_task(settings, environment):
        def select_environment(*roles):
            if hasattr(env, "environmentdef") and env.environmentdef.name != environment:
                abort("Environment already defined as '{}'".format(env.environmentdef.name))

            # Do not select hosts here.
            #
            # If `fab` is run with multiple hosts, this task will be run multiple
            # times, overwriting the value of "env.environmentdef". By not selecting hosts
            # here, we ensure that the same environmentdef will be loaded each
            # time. See also confab.iter:iter_conffiles
            env.environmentdef = settings.for_env(environment).with_roles(*roles)
        return select_environment

    settings = Settings.load_from_module(settings_path)

    for environment in settings.environmentdefs.iterkeys():
        _add_task(environment,
                  create_task(settings, environment),
                  "Set environment to '{environment}'".format(environment=environment))
    return settings

@@ -349,16 +349,27 @@
 ort, env
+, runs_once
 %0Afrom fa
@@ -505,16 +505,176 @@
 ic task.
+%0A%0A    The resulting task is declared runs_once to ensure that environment%0A    definitions are loaded properly when other (subsequent) tasks are run in parallel.
 %0A    %22%22%22
@@ -749,17 +749,17 @@
 er, 
-'
+%22
 __doc__
-'
+%22
 , do
@@ -782,16 +782,26 @@
 name%5D = 
+runs_once(
 task_wra
@@ -804,16 +804,17 @@
 _wrapper
+)
 %0A%0A%0Adef g

Use runs_once (explitly) so that generated environment tasks are run serially and only once in a larger, parallel environment.