Skip to content

Generic, higher kinded function not sharing Callable’s limitations #13107

@flying-sheep

Description

@flying-sheep

Documentation

I can define the following decorator:

P = ParamSpec('P')
R = TypeVar('R', covariant=True)

def file_cache(fn: Callable[Concatenate[Path, P], R]) -> Callable[P, R]:
    path: Path = ...
    def wrapper(*args: P.args, **kw: P.kwargs) -> R:
        if path.is_file():
            return read(path)
        return fn(path, *args, **kw)
    return wrapper

But Callable is limited to kwargs-less functions, so mypy complains when I apply the decorator to a function, and try to specify the keyword argument:

@file_cache
def get_thing(path: Path, *, some_arg: int) -> X:
    x = calculate_x()
    path.write_bytes(x.serialize())
    return x

if __name__ == '__main__':
    get_thing(some_arg=1)
error: Unexpected keyword argument "some_arg" for "get_thing"

In an attempt to circumvent the problem, I tried to define the following, but mypy doesn’t seem to understand it:

P = ParamSpec('P')
R = TypeVar('R', covariant=True)


class Function(Protocol[P, R]):
    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        ...

And will complain that the decorated function was a Callable[[Path, DefaultNamedArg(int, 'some_arg')], X, not a Function[<nothing>, <nothing>].

If I made a mistake in defining Function, please help.

If it looks correct: a) why doesn’t it exist / why is Callable so limited? and b) mypy should understand my definition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions