Bogey
Bogey

Reputation: 5724

get_type_hints for generic type

Assume the following class definition,

import typing
from dataclasses import dataclass

T1 = typing.TypeVar('T1')
T2 = typing.TypeVar('T2')

@dataclass
class Test(Generic[T1, T2]):
  some_property: T2
  some_other_property: T1

I would like to get the type hints of a specific bound version of this class, something like

# I would like to get back: {'some_property': str, 'some_other_property': int}
hints = typing.get_type_hints(Test[int,str])

Unfortunately, this don't work in python 3.9, raising a type error "Test[int,str] is not a module, class, method or function". Now, I could do something like

hints = typing.get_type_hints(typing.get_origin(Test[int,str]))

However, in that case, some_property and some_other_properties are returned as TypeVars, rather than specific bound types (int and str).

Binding these manually seems a little annoying; Test[int,str] is of type typing._GenericAlias, which seems to have a property _ _ args _ _ which is [int, str]. I could in theory try to bind them back to the type vars in the order in which they appear first from get_type_hints(get_origin()), but I'm not sure if that is reliable.

Is there any equivalent of get_type_hints that would return fully bound type hints? Or any reasonable other way of doing this?

Upvotes: 3

Views: 1943

Answers (1)

royce3
royce3

Reputation: 1422

I upgraded to 3.9.2 and it did not solve the problem for me.

There appears to be a limitation on the scope of what typing.get_type_hints() can do. I thought it very odd this sort of type construct doesn't seem to be a problem in other scenarios. Some quick testing found that it can handle your Test type just fine as a child of another class type.

I modified your example to create a new function, my_type_hints() and it dynamically creates a temporary class object with the provided type as it's only child element.

I believe the following solves your problem. I don't know if it's a comprehensive solution, but I believe it's probably more robust than digging into __args__, __origin__, etc.

from typing import Any, Generic, get_type_hints, TypeVar
from dataclasses import dataclass

T1 = TypeVar('T1')
T2 = TypeVar('T2')

@dataclass
class Test ( Generic[T1,T2] ):
    some_property: T2
    some_other_property: T1

def my_type_hints ( obj: Any ) -> Any:
    class X:
        x: obj
    return get_type_hints ( X )['x']

hints = my_type_hints ( Test[int,str] )
print ( f'{hints=}' )

Upvotes: 2

Related Questions