Handling Input and Outputs
==========================

``b2luigi`` comes with a number of helpful functionalities to simplify the handling of input and output targets.
You can check the general usage in the b2luigi :class:`b2luigi.Task` documentation.
In the following only some best practices are explained.

Outputs
-------

A tasks output can easily be defined using the task's ``output`` method in combination with its :meth:`b2luigi.Task.add_to_output` method.
:meth:`add_to_output <b2luigi.Task.add_to_output>` create a ``luigi`` target with a unique path depending on the tasks parameter.
This way the book keeping of all output files of a task tree is already taken care of.
To access the output target, the task class has the :meth:`b2luigi.Task.get_output_file` method.
This method always returns the full path of the output file.

.. attention::
    Only use the :meth:`get_output_file <b2luigi.Task.get_output_file>` method to access the output file.
    This will ensure, that the correct file us used.
    If you want to work with temporary files, use the :meth:`b2luigi.on_temporary_file` decorator.

Inputs
------

A tasks inputs can be accessed via the :meth:`b2luigi.Task.get_input_file_names` method.
Since a task can have multiple inputs of the same type (e.g.: different background processes run through the same selection), :meth:`get_input_file_names <b2luigi.Task.get_input_file_names>` always returns a list.

Sometimes a Task requires multiple input files from various previous tasks.
Handling these can get tedious.
In this case, :meth:`b2luigi.Task.get_input_file_names_from_dict` can help.
This method expects the task's ``requires`` method to return a dictionary of the form:

.. code-block:: python

    def requires(self):
        return {
            "input_a": (Task_A(), Task_B()),
            "input_b": (Task_C(), Task_D()),
            ...
        }

It then allows to access the list of all inputs generated by ``Task_A``, ``Task_B``, ... via the key ``input_a``.
