Overrides the parent method to find parsers appropriate to a given extension and type.
        This leverages both the parser registry and the converter registry to propose parsing chains in a relevant order

        :param strict:
        :param desired_type: the type of object to match.
        :param required_ext: the required extension.
        :return: match=(matching_parsers_generic, matching_parsers_approx, matching_parsers_exact),
                 no_type_match_but_ext_match, no_ext_match_but_type_match, no_match

def find_all_matching_parsers(self, strict: bool, desired_type: Type[Any] = JOKER, required_ext: str = JOKER) \
        -> Tuple[Tuple[List[Parser], List[Parser], List[Parser]],
                 List[Parser], List[Parser], List[Parser]]:
        """
        Overrides the parent method to find parsers appropriate to a given extension and type.
        This leverages both the parser registry and the converter registry to propose parsing chains in a relevant order

        :param strict:
        :param desired_type: the type of object to match.
        :param required_ext: the required extension.
        :return: match=(matching_parsers_generic, matching_parsers_approx, matching_parsers_exact),
                 no_type_match_but_ext_match, no_ext_match_but_type_match, no_match
        """
        # transform any 'Any' type requirement into the official class for that
        desired_type = get_validated_type(desired_type, 'desired_type', enforce_not_joker=False)

        # (1) call the super method to find all parsers
        matching, no_type_match_but_ext_match, no_ext_match_but_type_match, no_match = \
            super(ParserRegistryWithConverters, self).find_all_matching_parsers(strict=self.is_strict,
                                                                                desired_type=desired_type,
                                                                                required_ext=required_ext)
        # these are ordered with 'preferred last'
        matching_p_generic, matching_p_approx, matching_p_exact = matching

        if desired_type is JOKER:
            # then we want to try to append every possible converter chain, even if we have already found an exact match
            # (exact match will probably contain all parsers in that case?...)
            parsers_to_complete_with_converters = no_type_match_but_ext_match + matching_p_generic + matching_p_approx \
                                                  + matching_p_exact
        else:
            # then we can try to complete all the ones matching the extension (even the generic because combining them
            # with a conversion chain might provide another way to reach the result - different from using the generic
            # alone to reach the to_type)
            parsers_to_complete_with_converters = no_type_match_but_ext_match + matching_p_generic + matching_p_approx

        # (2) find all conversion chains that lead to the expected result
        matching_c_generic_to_type, matching_c_approx_to_type, matching_c_exact_to_type = \
            self.get_all_conversion_chains_to_type(to_type=desired_type)
        all_matching_converters = matching_c_generic_to_type + matching_c_approx_to_type + matching_c_exact_to_type

        # (3) combine both (parser + conversion chain), and append to the appropriate list depending on the match
        # -- (a) first Parsers matching EXT (not type) + Converters matching their type
        # for all parsers able to parse this extension, and for all the types they support
        #
        # (we have to reverse the list because now we want 'preferred first'. Indeed we wish to prepend to the match
        # lists in order not to hide the parser direct matches)
        matching_p_generic_with_approx_chain, matching_p_approx_with_approx_chain, matching_p_exact_with_approx_chain\
            = [], [], []
        for parser in reversed(parsers_to_complete_with_converters):
            for typ in parser.supported_types:
                match_results = self._complete_parsers_with_converters(parser, typ, desired_type,
                                                                       matching_c_generic_to_type,
                                                                       matching_c_approx_to_type,
                                                                       matching_c_exact_to_type)
                # prepend the existing lists with the new match
                matching_p_generic = match_results[1] + matching_p_generic
                matching_p_approx = match_results[3] + matching_p_approx
                matching_p_exact = match_results[5] + matching_p_exact

                # store the approximate matchs in the separate lists
                matching_p_generic_with_approx_chain = match_results[0] + matching_p_generic_with_approx_chain
                matching_p_approx_with_approx_chain = match_results[2] + matching_p_approx_with_approx_chain
                matching_p_exact_with_approx_chain = match_results[4] + matching_p_exact_with_approx_chain

        # finally prepend the approximate match lists
        matching_p_generic = matching_p_generic_with_approx_chain + matching_p_generic
        matching_p_approx = matching_p_approx_with_approx_chain + matching_p_approx
        matching_p_exact = matching_p_exact_with_approx_chain + matching_p_exact

        # -- (b) then parsers that do not match at all (not the file extension nor the type): we can find parsing chains
        # that make them at least match the type
        #
        # (we have to reverse it because it was 'best last', now it will be 'best first')
        for parser in reversed(no_match):
            for typ in parser.supported_types:
                for converter in reversed(all_matching_converters):
                    # if converter is able to source from this parser
                    if converter.is_able_to_convert(self.is_strict, from_type=typ, to_type=desired_type):
                        if ParsingChain.are_worth_chaining(parser, typ, converter):
                            # insert it at the beginning since it should have less priority
                            no_ext_match_but_type_match.insert(0, ParsingChain(parser, converter, strict=self.is_strict,
                                                                               base_parser_chosen_dest_type=typ))

        # Finally sort by chain length
        matching_p_generic = sorted(matching_p_generic, key=len, reverse=True)
        matching_p_approx = sorted(matching_p_approx, key=len, reverse=True)
        matching_p_exact = sorted(matching_p_exact, key=len, reverse=True)

        # Return
        return (matching_p_generic, matching_p_approx, matching_p_exact), no_type_match_but_ext_match, \
               no_ext_match_but_type_match, no_match