-
I'm trying to implement a function that receives either a simple type (from a list of acceptable types) or a composite type between the simple type and None and returns the base type, i.e. either the simple type or extract the base type from the composite type. Note that the function doesn't deal with values of those types, but with the types themselves. As an example For this I have the following code: from types import NoneType, UnionType
from typing import cast, reveal_type
type XMLData = str | int | float | bool
def get_base_type[D: XMLData](data_type: type[D] | UnionType) -> type[D]:
if isinstance(data_type, UnionType):
if len(data_type.__args__) != 2 or NoneType not in data_type.__args__:
raise TypeError(f'Unsupported data type: {data_type!r}')
try:
base_type = cast(type[D], next(t for t in data_type.__args__ if issubclass(t, XMLData.__value__)))
except StopIteration:
raise TypeError(f'Unsupported data type: {data_type!r}') from None
else:
return reveal_type(base_type) # Type of "base_type" is "type[D@get_base_type]"
return data_type
t1 = reveal_type(get_base_type(int)) # Type of "get_base_type(int)" is "type[int]"
t2 = reveal_type(get_base_type(str | None)) # Type of "get_base_type(str | None)" is "Unknown"
t3: type[str] = reveal_type(get_base_type(str | None)) # Type of "get_base_type(str | None)" is "type[str]" I added comments on the relevant lines with what pyright reports about the types via the call to reveal_type. Now I understand I have to add that cast for base_type because I don't expect pyright to be able to understand my runtime introspection into the composite type. Without that cast base_type appears of being of type Any. What surprises me is that even with the cast and despite the fact that pyright correctly reports the type of base_type as I'm not sure if this is a bug or I'm misunderstanding something here, but how can the type before return be something and right after return become something else? Pyright doesn't report any errors for this code, which makes it even more confusing, because despite the function being annotated to return I also checked with mypy and mypy reports something similar: it says that I need a type annotation for t2 and it claims that the type for t2 is |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
I now realize that this may be because the UnionType branch doesn't involve D on input, so it has no idea what |
Beta Was this translation helpful? Give feedback.
When a static type checker evaluates a call to a function, it uses only the function's signature (i.e. the parameter and return type annotations supplied in the function declaration) to evaluate the type returned by the function. It does not pay attention to the body of the function. In fact, you can omit the entire function body and replace it with
...
, as is often done in type stub files. The types of the local variables and expressions within a function body (e.g. where you are callingreveal_type
) have no bearing on the evaluation of calls toget_base_type
.One of the problems here is that you're using type forms that look like type expressions (such as
str | None
), but you're using t…