Mypy shows error when using a function to return a type

293 Views Asked by At

I have written a function string_prompt to return a type annotation which I have to reuse a lot in the context of typer. This is a working example (run with python -m):

from typing import Annotated

import typer

app = typer.Typer()


def string_prompt(prompt: bool | str = True):
    return Annotated[str, typer.Option(prompt=prompt)]


@app.command()
def public(name: string_prompt(), project_number: string_prompt()):
    print(f"You called public with {name}, {project_number}")

if __name__ == "__main__":
    app()

This code works, but mypy shows the error:

Invalid type comment or annotation (valid-type). Suggestion: use string_prompt[...] instead of string_prompt(...)

I've tried using square brackets instead, which obviously returns a syntax error, since I can't use square brackets to call a function.

Now my question is: Is there a different way I should declare this function to make mypy understand what I am trying to achieve? I had a look at the documentation of the mypy error, in which it explains, that functions are not a valid type, but the solution proposed which uses Callable is not applicable in my case, since I want to actually use the return value of the function called.

1

There are 1 best solutions below

6
Jasmijn On BEST ANSWER

This is not exactly what you wanted, but you can avoid type annotations getting overly long with type aliases.

For Python <= 3.11:

from typing import TypeAlias

string_prompt: TypeAlias = Annotated[str, typer.Option(prompt=True)]

@app.command()
def public(name: string_prompt, project_number: string_prompt) -> None:
    print(f"You called public with {name}, {project_number}")

For Python 3.12+:

type string_prompt = Annotated[str, typer.Option(prompt=True)]

@app.command()
def public(name: string_prompt, project_number: string_prompt) -> None:
    print(f"You called public with {name}, {project_number}")

The main limitation here is that you cannot pass arguments to string_prompt and thus cannot use another value for prompt. You could do that with "arguments" that are types, for example:

# Python <= 3.11
from typing import TypeAlias, TypeVar
T = TypeVar('T')
prompt: TypeAlias = Annotated[T, typer.Option(prompt=True)]

# Python 3.12+
type prompt[T] = Annotated[T, typer.Option(prompt=True)]

... but the second argument to Annotated is a runtime value, ignored by type checkers and thus can't be parameterised by type checkers.