seandstewart / typical

Typical: Fast, simple, & correct data-validation using Python 3 typing.
https://python-typical.org
MIT License
182 stars 9 forks source link

TypeVar Support #119

Closed seandstewart closed 1 week ago

seandstewart commented 4 years ago

Description

Typical currently stumbles over classes which make use of TypeVar in their definitions, such as Generics. At the very least, we should be able to treat anonymous (unbound) typevars as "Any", and at best, map bound TypeVars correctly.

Implementation

  1. Implement TypeVar resolution, perhaps in Resolver.annotation, to determine whether this should be treated as a Union or Any. If the TypeVar is constrained, treat it as a Union for resolution, otherwise, an Any:
# If done as a branch in `Resolver.annotation`
if use.__class__ is TypeVar:
    if use.__constraints__:
        use = Union[*use.__constraints__]
    else:
        use = Any

For consideration - do we want to create a NewType for this Union?

We probably also want to retain a flag on the ResolvedAnnotation that signals this came from a TypeVar.

  1. Implement resolution for Generics - this will take additional experimentation, and will likely need to tweak internal funcsigs. The easiest way to pre-emptively map subscripted generics to their given bound types:
import typic
from typing import Generic, TypeVar, get_type_hints
import dataclasses
T = TypeVar("T")
@dataclasses.dataclass
class Foo(Generic[T]):
    bar: T

FooStrT = Foo[str]

bindings = dict(zip(Foo.__parameters__, FooStrT.__args__))
# If no bindings, our TypeVar resolution works for us. Otherwise, we should override the TypeVar anno with the binding.
hints = {name: bindings.get(t, t) for name, t in get_type_hints(FooStrT)}