from typing import Callable
from typing import List
from typing import Any


def _curry_helper(
    number_of_arguments: int,
    function_to_curry: Callable,
    accumulated_arguments: List[Any],
) -> Callable:
    """ Builds a curried version of the supplied function and returns it to the caller.

    Args:
        number_of_arguments: specifies how many arguments 'function_to_curry' takes as input.
        function_to_curry: a function or other callable object either built-in or user defined.
        accumulated_arguments: a list containing all the arguments so
            far supplied to 'function_to_curry'. For instance, if
            'function_to_curry' takes two arguments but has been called with
            only one so far, then accumulated_arguments will contain a
            single item.

    Returns:
        A new function which may be partially applied simply by passing
        the desired number of arguments.
    """

    def _curry_internal(*arguments: List[Any]):
        """ Handles the actual partial application of curried functions.

        Args:
            *arguments: a variable number of arguments to be
                (eventually) supplied to the wrapped function
                'function_to_curry'

        Returns:
            Either the result of calling the wrapped function_to_curry
            with the total accumulated arguments or the result of
            calling _curry_helper with the new, larger
            accumulated_arguments list.
        """
        all_arguments = accumulated_arguments[:]
        all_arguments.extend(arguments)
        if len(all_arguments) >= number_of_arguments:  # pylint: disable=no-else-return
            return function_to_curry(*all_arguments)
        else:
            return _curry_helper(number_of_arguments, function_to_curry, all_arguments)

    return _curry_internal