from typing import Callable
from typing import Iterable
from typing import Tuple
from typing import List
import inspect
import itertools


def override_args_kwargs(f: Callable, args: Iterable, kwargs: dict, new_kwargs: dict) -> Tuple[List, dict]:
    """Overrides positional and keyword arguments according to signature of the function using new keyword arguments.

    Args:
        f: callable, which signature is used to determine how to override arguments.
        args: original values of positional arguments.
        kwargs: original values of keyword arguments.
        new_kwargs: new keyword arguments, their values will override original arguments.

    Return:
        args: updated list of positional arguments.
        kwargs: updated dictionary of keyword arguments.
    """
    args = list(args)
    new_kwargs = new_kwargs.copy()
    p = inspect.signature(f).parameters
    for idx, (k, v) in enumerate(itertools.islice(p.items(), len(args))):
        if v.kind not in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD):
            break
        if k in new_kwargs:
            args[idx] = new_kwargs.pop(k)
    return args, {**kwargs, **new_kwargs}