pythonmypypython-typingpython-click

Missing type parameters for generic type "Callable"


What is the correct way to add type hints to the following function?

from typing import Callable

def format_callback(f: Callable) -> Callable:
    """Function to wrap a function to use as a click callback.

    Taken from https://stackoverflow.com/a/42110044/8056572
    """
    return lambda _, __, x: f(x)

Now mypy is complaining with Missing type parameters for generic type "Callable"

The code needs to be compatible with both Python 3.9 and 3.10. I can use typing_extensions if needed.

Edit:

The following passes mypy but has too many Any's for my taste. Is there a better way?

from typing import Any
from typing import Callable

import click


def format_callback(f: Callable[[Any], Any]) -> Callable[[click.Context, dict[str, Any], Any], Any]:
    """Function to wrap a function to use as a click callback.

    Taken from https://stackoverflow.com/a/42110044/8056572
    """
    return lambda _, __, x: f(x)

Solution

  • Without looking at click, the immediate fix you can do is to provide type variables that match f(x):

    from typing import Any, Callable, TypeVar
    
    import click
    
    ArgT = TypeVar("ArgT")
    ReturnT = TypeVar("ReturnT")
    
    def format_callback(
        f: Callable[[ArgT], ReturnT]
    ) -> Callable[[click.Context, dict[str, Any], ArgT], ReturnT]:
        return lambda _, __, x: f(x)
    

    This will guard you against bad typing in the internal body of format_callback.


    A brief scan of click seems to indicate that you want to pass the return value of format_callback to one of the following class constructors or their subclass constructors:

    Now, based on the other answer that you linked to, which passes the keyword argument callback to @click.argument and @click.option, it seems like you'd actually want to use click.core.Parameter.__init__, because the decorators' fallback classes are ArgumentClass = click.core.Argument and OptionClass = click.core.Option, respectively, which are both subclasses of click.Core.Parameter. This means that the second argument to the return Callable type cannot be dict[str, Any], according to the type annotation for click.core.Parameter.__init__::callback. Then, you really should have this instead:

    def format_callback(
        f: Callable[[ArgT], ReturnT]
    ) -> Callable[[click.Context, click.Parameter, ArgT], ReturnT]:
        return lambda _, __, x: f(x)
    

    Since you're discarding click.Context and click.Parameter as _, __ in your lambda, providing these are only for documentation purposes, of course. They could easily be object, object instead.