Skip to content

tytr

Some useful type transformations for Python, mostly focused on TypedDict types.

Usage

tytr provides two kinds of tools: tools for generating types, and tools for generating tests that check your types against a generated type.

tytr should probably not be a runtime dependency of your library. Instead it's used at "writetime" (code generation) and "testtime" (consistency checks).

Creating types

Suppose you have a class like this:

# foo.py
class Foo:
    bar: int
    baz: float

Maybe you need to serialize instances of Foo to JSON, or you want to allow construction of Foo via plain dicts. Then you might want a dict-based model of Foo, to make your API based on JSON or plain-dicts type safe.

Specifically, you need a TypedDict based on the attributes and type annotations of Foo.

tytr will make this TypedDict for you.

$ tytr gen typeddict foo.py::Foo --name FooDict

# Generated by tytr gen typeddict from Foo
from __future__ import annotations
from typing_extensions import TypedDict

class FooDict(TypedDict):
    bar: int
    baz: float

Testing types

To keep this TypedDict model synchronized with changes to the source class, you could re-generate code, but it might be simpler to use tytr to generate tests that check your type.

# some file in your automated test suite
from tytr.testing import make_type_test

# create a test that checks if Foo is the same as FooDict
test_dict_type = make_type_test(Foo, FooDict, transform="typeddict")

Concepts

If you've used TypeScript then you might be familiar with useful type transformations like KeyOf or the various utility types.

tytr implements type transformations like Typescript's KeyOf. The types generated by tytr are conditionally defined because they depend on the structure of some source type.

For example, the output of the key_of(X) function naturally depends on the keys of X.

from typing_extensions import TypedDict
from typing import Literal
from tytr import key_of

class X(TypedDict):
    a: int

K = key_of(X)
assert K == Literal["a"]
In the above example, K is not a TypeAlias. It's just a regular Python variable that happens to have type type[...]. So K is useless as a type annotation, which is why tytr uses test generation instead of a type checker.

It would be great for the Python type system to get functionality like KeyOf. Until then, tytr offers the next best solution.