[Python] Issue while typing a recursive function with list unpacking

Discussão em 'Python' iniciado por Stack, Junho 19, 2025.

  1. Stack

    Stack Membro Participativo

    I'm trying to correctly type the following function in Python 3.10

    from typing import overload

    @overload
    def joinpath(*path_pieces: str, all_pieces_are_str: bool) -> str: ...
    @overload
    def joinpath(*path_pieces: list[str] | str, all_pieces_are_str: bool) -> list[str]: ...
    def joinpath(*path_pieces: list[str] | str, all_pieces_are_str: bool = False) -> list[str] | str:
    """Join paths.

    os separators in the path pieces will be remove, except for the trailing
    separator of the last piece which is used to differenciate folders from
    files.

    Parameters
    ----------
    *path_pieces : str | list[str]
    Path pieces to join. If one of the path piece is a list, the function
    will join each element of the list with the other path pieces and return
    a list of path. If all path pieces are strings, the function will return
    a string.

    Returns
    -------
    str | list[str]
    the joined path: str if all the components are strings, list of strings
    otherwise.

    Examples
    --------
    >>> joinpath("a", "b", "c")
    "a/b/c"
    >>> joinpath("a", "", "c")
    "a/c"
    >>> joinpath(["a", "b"], "c/")
    ["a/c/", "b/c/"]
    >>> joinpath(["a", "b"], ["c", "d"], "e")
    ["a/c/e", "a/d/e", "b/c/e", "b/d/e"]
    >>> joinpath(["a", "", "b"], ["c", "d"], "e")
    ["a/c/e", "a/d/e", "b/c/e", "b/d/e"]
    """
    if all(isinstance(piece, str) for piece in path_pieces):
    all_pieces_are_str = True

    if len(path_pieces) < 2:
    raise ValueError("At least two path pieces are required.")
    if len(path_pieces) > 2:
    # Recursive call to join the first two pieces and the rest of the pieces.
    # This will slowly reduce the number of pieces until there are only two.
    return joinpath(
    joinpath(path_pieces[0], path_pieces[1]),
    *path_pieces[2:],
    all_pieces_are_str=all_pieces_are_str,
    )

    first_paths = ensure_in_list(path_pieces[0])
    second_paths = ensure_in_list(path_pieces[1])

    joined_paths = _cartesian_product_join_paths(first_paths, second_paths)

    if all_pieces_are_str is True:
    return joined_paths[0]

    return joined_paths


    def ensure_in_list(value: T | list[T]) -> list[T]:
    """Put in a list any scalar value that is not None.

    If it is already a list, do nothing.
    """
    if value is not None and not isinstance(value, list):
    value = [value]

    return value


    def _cartesian_product_join_paths(
    first_path_piece: list[str], second_path_piece: list[str]
    ) -> list[str]:
    """Compute the cartesian product of two lists of paths.

    If one of the path piece is an empty string, it will be ignored.

    The output list will contain the concatenation of each element of the first
    list with each element of the second list.

    Returns
    -------
    list[str]
    List of length len(first_path_piece) * len(second_path_piece) that
    contains the concatenated path.
    """
    ...


    Unfortunately, there are three mypy errors:

    1. L6 error: Overloaded function signatures 1 and 2 overlap with incompatible return types
    2. L50 error: No overload variant of "joinpath" matches argument types "list[str] | str", "list[str] | str"
    3. L51 error: Argument 2 to "joinpath" has incompatible type "*tuple[list[str] | str, ...]"; expected "str"

    For the first error, the two signatures are meant to reflect that if there is at least one list of str as an input, the output will be a list.

    For the second error and third error, I feel like types are compatible, so I'm not sure what to do.

    Continue reading...

Compartilhe esta Página