How to use TypeVar bounded type attributes in Generic class type hinting

  pydantic, python-3.x, python-typing

Let’s start from an example (simple and academic):

from typing import ClassVar, TypeVar, Generic, Any, Type

class Options:
   pass

class Foo:
   Opts: ClassVar[Options]

FooType = TypeVar('FooType', bound=Foo)

class GenericModel(Generic[FooType]):
   opts: FooType.Opts    # <-- not working

   def configure(self, foo: Type[FooType]):
       self.opts = foo.Opts

   def another_use_case(self, opts: FooType.Opts):  # <-- not working
       # an argument is something from FooType
       pass

# usage

class FooBar(Foo):
   class Opts(Options):
      optA: Any

wrap = Wrapper[FooBar]()
wrap.configure(FooBar)

Is it possible to annotate the type of opts?

Python complains that TypeVar has not attribute Opts. I found that when TypeVar is bounded, the bound is stored in __bound__ but it looks like there is no overloaded getattr on TypeVar class to access attributes from the bound type.

Do I made a mistake somewhere or such cases are not supported by Python typing at all? Is there any workaround for this? I can imagine more cases when access to TypeVar bounded type attributes types might be useful in generic class typing.

Real use case for this is much more complex and depends on Pydantic. GenericModel is a Pydantic model which I’m trying to make generic as I have a set of very similar models which differs mostly on types, so instead of having FooModel, BarModel and so on, I want to make one GenericModel class and concretize them by writing foo_model = GenericModel[Foo](), bar_model = GenericModel[Bar]().
In Pydantic to validate opts automatically, it must be correctly type annotated. Also with such type hinting, someone else exactly knows what opts should be.

The only workaround I found as far is typing opts as Any and making a validation in custom validator, however it kind a magic and makes code very error prone as it’s not clear that opts is not in fact an Any. The next disadvantage is that this model is used as payload in FastAPI so I’ve lost all type schema in generated OpenAPI spec.

Source: Python-3x Questions

LEAVE A COMMENT