pythonpython-typingmypy

How to check type compatibility when using the typing module?


I'm using Python PEP484 type hints to write a type-checker for a DSL I've written in Python. If I have a function expecting type T for one of its arguments, and it's being called with an expression of type S, how can I check whether the call is valid? Is using issubclass(S, T) enough? If so, why does mypy have such a complicated is_subtype check? Or should I just use the mypy version?

Edit: Here's an example to clarify what I mean. The DSL has a function defined as:

T = TypeVar('T', float, str)
def op_add(operand1: T, operand2: T) -> T:
    "Number addition or string concatenation."
    # In this DSL, `+` cannot be used with lists
    return operand1 + operand2  # Rely on Python overloading of `+`

Then a user types in an expression which is parsed into a syntax tree, with a branch that could be: node = OperatorNode('+', Literal([5.0]), Variable("abc")). We don't know the value of the abc variable yet, but lists can never be used with +, so I want to raise a TypeError to alert the user.

If I do issubclass(typing.List[float], var), that gives me False, so I can raise an error right away. My question is whether this check is guaranteed to work across cases as I build out the DSL, or if I need to use a more complex check like mypy


Solution

  • issubclass check is sufficient if neither argument to issubclass includes constructs from typing module such as Union, Callable, Any, generics, etc.

    typing constructs exist in python runtime as a shadow of their true form, that is they don't support many of the operations that make sense conceptually:

    issubclass(List[int], List[int])  # runtime error
    issubclass(List[int], List) # True (as expected)
    issubclass(str, Union[str]) # runtime error
    issubclass(Union[str], str) # True (as expected)
    issubclass(Union[int, str], str) # runtime error
    

    Sometimes issubclass will work with typing constructs, but in general, it may raise an exception or give an incorrect answer; you'll need to figure out what to do on a case by case basis.

    mypy has a more complicated is_subtype because it does need to handle all the typing constructs, and even then there is still some work to be done there.