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>` creates 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 filename, the task class has the :meth:`b2luigi.Task.get_output_file_name` 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_name>` method to access the output file.
    This will ensure, that the correct file is used.
    If you want to work with temporary files, use the :meth:`b2luigi.on_temporary_files` decorator.
    As long as you fully commit to these tasks own methods, you do not have to change any of your code when working with temporary files.

To find out more about the task and its methods, check the :class:`b2luigi.Task` documentation.
The temporary file decorator is documented in the :ref:`Temporary File Context Manager <api-on-temporary-label>` section.

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 one to access the list of all inputs generated by ``Task_A``, ``Task_B``, ... via the key ``input_a``.
