Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python TypeVar Reset in Tuple

I have a problem with my TypeVar definition.
My function requires a tuple of tuples as a parameter. The main tuple can have as many as tuples as components but they don't have to share the same type between them.
But the component of the tuple inside the main tuple have to share the same type.

Here a simplified example of code:
The following code work in python but I have a warning in Pycharm

form((("prompt", str, "default"),
      ("prompt", int, 10),
      ("prompt", str, "default"),
      ("prompt", float, .5)))

PyCharm Warning

I defined my function like that:

ValueType = TypeVar('ValueType', str, int, float, bool)
InputDefinitionType = Tuple[str, Type[ValueType], ValueType]

def form(input_def: Tuple[InputDefinitionType, ...])
    ...

How can I 'reset' the TypeVar group to only match the types inside the tuple contained inside the main tuple but not across the main tuple itself?

like image 677
olive007 Avatar asked Feb 23 '26 15:02

olive007


1 Answers

tl;dr: The way you're implementing it is wrong, but there's a workaround at the bottom of this answer.

Unfortunately, your code does not type-check. A constrained type variable must refer to the same type within a given "scope" (PEP 484 has some definitions on scope but it's not too precise).

In your case, ValueType must refer to a single type within the form function, so your call is not valid.


As to why the code seems to type-check in mypy, it's because InputDefinitionType is a generic type alias, but you're using it without parameterization, which is then equivalent to parameterizing with Any. Put simply:

def form(input_def: Tuple[InputDefinitionType, ...]) -> None: ...

is equivalent to

def form(input_def: Tuple[InputDefinitionType[Any], ...]) -> None: ...

You can see this in mypy-play, where the final reveal_type(form) shows the inferred type of the function to be:

main.py:15: note: Revealed type is "def (input_def: builtins.tuple[Tuple[builtins.str, Type[Any], Any]])"

A workaround for this is to turn it into a Union type:

ValueType = TypeVar('ValueType')
InputDef = Tuple[str, Type[ValueType], ValueType]  # name shortened for conciseness
InputDefConcrete = Union[
  InputDef[float], InputDef[str], InputDef[bool], InputDef[int]
]

def form(input_def: Tuple[InputDefConcrete, ...]) -> None: ...

See it in action on mypy-play.

like image 87
Zecong Hu Avatar answered Feb 26 '26 04:02

Zecong Hu