Utility method to chain two converters. If any of them is already a ConversionChain, this method "unpacks" it
        first. Note: the created conversion chain is created with the provided 'strict' flag, that may be different
        from the ones of the converters (if compliant). For example you may chain a 'strict' chain with a 'non-strict'
        chain, to produce a 'non-strict' chain.

        :param first_converter:
        :param second_converter:
        :param strict:
        :return:

def chain(first_converter, second_converter, strict: bool):
        """
        Utility method to chain two converters. If any of them is already a ConversionChain, this method "unpacks" it
        first. Note: the created conversion chain is created with the provided 'strict' flag, that may be different
        from the ones of the converters (if compliant). For example you may chain a 'strict' chain with a 'non-strict'
        chain, to produce a 'non-strict' chain.

        :param first_converter:
        :param second_converter:
        :param strict:
        :return:
        """
        if isinstance(first_converter, ConversionChain):
            if isinstance(second_converter, ConversionChain):
                # BOTH are chains
                if (first_converter.strict == strict) and (second_converter.strict == strict):
                    return first_converter.add_conversion_steps(second_converter._converters_list)
                else:
                    if not strict:
                        # create a non-strict chain
                        return ConversionChain(initial_converters=first_converter._converters_list,
                                               strict_chaining=False) \
                            .add_conversion_steps(second_converter._converters_list)
                    else:
                        raise ValueError('Trying to chain conversion chains with different strict modes than expected')

            else:
                # FIRST is a chain
                if strict == first_converter.strict:
                    return first_converter.add_conversion_step(second_converter)
                else:
                    if not strict:
                        # create a non-strict chain
                        return ConversionChain(initial_converters=[second_converter], strict_chaining=False) \
                            .insert_conversion_steps_at_beginning(first_converter._converters_list)
                    else:
                        raise ValueError('Trying to chain after a conversion chain that has different strict mode than '
                                         'expected')

        else:
            if isinstance(second_converter, ConversionChain):
                # SECOND is a chain
                if strict == second_converter.strict:
                    return second_converter.insert_conversion_step_at_beginning(first_converter)
                else:
                    if not strict:
                        # create a non-strict chain
                        return ConversionChain(initial_converters=[first_converter], strict_chaining=False) \
                            .add_conversion_steps(second_converter._converters_list)
                    else:
                        raise ValueError(
                            'Trying to chain before a conversion chain that has different strict mode than '
                            'expected')
            else:
                # Neither is a chain
                return ConversionChain([first_converter, second_converter], strict)