'Initialise with the task dependencies.
A task to run on each dependency may optionally be specified.  If no
task is supplied, it is assumed that the tasks are stored directly in
the dependency tree. If a task is supplied, the object stored in the
dependency tree is passed as an argument.
If an error_wait_time is specified, tasks that are already running at
the time of an error will continue to run for up to the specified time
before being cancelled. Once all remaining tasks are complete or have
been cancelled, the original exception is raised. If error_wait_time is
a callable function it will be called for each task, passing the
dependency key as an argument, to determine the error_wait_time for
that particular task.
If aggregate_exceptions is True, then execution of parallel operations
will not be cancelled in the event of an error (operations downstream
of the error will be cancelled). Once all chains are complete, any
errors will be rolled up into an ExceptionGroup exception.'

def __init__(self, dependencies, task=(lambda o: o()), reverse=False, name=None, error_wait_time=None, aggregate_exceptions=False):

    self._keys = list(dependencies)
    self._runners = dict(((o, TaskRunner(task, o)) for o in self._keys))
    self._graph = dependencies.graph(reverse=reverse)
    self.error_wait_time = error_wait_time
    self.aggregate_exceptions = aggregate_exceptions
    if (name is None):
        name = ('(%s)    %s' % (getattr(task, '__name__', task_description(task)), six.text_type(dependencies)))
    self.name = name
