def fit(self, X=None, y=None, **kwargs):
        """Fit the blocks of this pipeline.

        Sequentially call the `fit` and the `produce` methods of each block,
        capturing the outputs each `produce` method before calling the `fit`
        method of the next one.

        During the whole process a context dictionary is built, where both the
        passed arguments and the captured outputs of the `produce` methods
        are stored, and from which the arguments for the next `fit` and
        `produce` calls will be taken.

        Args:
            X: Fit Data, which the pipeline will learn from.
            y: Fit Data labels, which the pipeline will use to learn how to
               behave.
            **kwargs: Any additional keyword arguments will be directly added
                      to the context dictionary and available for the blocks.
        """
        context = {
            'X': X,
            'y': y
        }
        context.update(kwargs)

        last_block_name = list(self.blocks.keys())[-1]
        for block_name, block in self.blocks.items():
            LOGGER.debug("Fitting block %s", block_name)
            try:
                fit_args = self._get_block_args(block_name, block.fit_args, context)
                block.fit(**fit_args)
            except Exception:
                LOGGER.exception("Exception caught fitting MLBlock %s", block_name)
                raise

            if block_name != last_block_name:
                LOGGER.debug("Producing block %s", block_name)
                try:
                    produce_args = self._get_block_args(block_name, block.produce_args, context)
                    outputs = block.produce(**produce_args)

                    output_dict = self._get_outputs(block_name, outputs, block.produce_output)
                    context.update(output_dict)
                except Exception:
                    LOGGER.exception("Exception caught producing MLBlock %s", block_name)
                    raise
Fit the blocks of this pipeline.

        Sequentially call the `fit` and the `produce` methods of each block,
        capturing the outputs each `produce` method before calling the `fit`
        method of the next one.

        During the whole process a context dictionary is built, where both the
        passed arguments and the captured outputs of the `produce` methods
        are stored, and from which the arguments for the next `fit` and
        `produce` calls will be taken.

        Args:
            X: Fit Data, which the pipeline will learn from.
            y: Fit Data labels, which the pipeline will use to learn how to
               behave.
            **kwargs: Any additional keyword arguments will be directly added
                      to the context dictionary and available for the blocks.