Trycast
trycast parses JSON-like values whose shape is defined by TypedDicts and other standard Python type hints.
Install / Use
/learn @davidfstr/TrycastREADME
trycast
<img src="https://raw.githubusercontent.com/davidfstr/trycast/main/README/trycast-logo.svg" title="trycast logo" align="right" />Trycast helps parses JSON-like values whose shape is defined by typed dictionaries (TypedDicts) and other standard Python type hints.
You can use the trycast(), checkcast(), or isassignable() functions below
for parsing:
trycast()
Here is an example of parsing a Point2D object defined as a TypedDict
using trycast():
from bottle import HTTPResponse, request, route # Bottle is a web framework
from trycast import trycast
from typing import TypedDict
class Point2D(TypedDict):
x: float
y: float
name: str
@route('/draw_point')
def draw_point_endpoint() -> HTTPResponse:
request_json = request.json # type: object
if (point := trycast(Point2D, request_json)) is None:
return HTTPResponse(status=400) # Bad Request
draw_point(point) # type is narrowed to Point2D
return HTTPResponse(status=200)
def draw_point(point: Point2D) -> None:
...
In this example the trycast function is asked to parse a request_json
into a Point2D object, returning the original object (with its type narrowed
appropriately) if parsing was successful.
More complex types can be parsed as well, such as the Shape in the following
example, which is a tagged union that can be either a Circle or Rect value:
from bottle import HTTPResponse, request, route
from trycast import trycast
from typing import Literal, TypedDict
class Point2D(TypedDict):
x: float
y: float
class Circle(TypedDict):
type: Literal['circle']
center: Point2D # a nested TypedDict!
radius: float
class Rect(TypedDict):
type: Literal['rect']
x: float
y: float
width: float
height: float
Shape = Circle | Rect # a Tagged Union!
@route('/draw_shape')
def draw_shape_endpoint() -> HTTPResponse:
request_json = request.json # type: object
if (shape := trycast(Shape, request_json)) is None:
return HTTPResponse(status=400) # Bad Request
draw_shape(shape) # type is narrowed to Shape
return HTTPResponse(status=200) # OK
Important: Current limitations in the mypy typechecker require that you add an extra
cast(Optional[Shape], ...)around the call totrycastin the example so that it is accepted by the typechecker without complaining:shape = cast(Optional[Shape], trycast(Shape, request_json)) if shape is None: ...These limitations are in the process of being resolved by introducing TypeForm support to mypy.
checkcast()
checkcast() is similar to trycast() but instead of returning None
when parsing fails it raises an exception explaining why and where the
parsing failed.
Here is an example of parsing a Circle object using checkcast():
>>> from typing import Literal, TypedDict
>>> from trycast import checkcast
>>>
>>> class Point2D(TypedDict):
... x: float
... y: float
...
>>> class Circle(TypedDict):
... type: Literal['circle']
... center: Point2D # a nested TypedDict!
... radius: float
...
>>> checkcast(Circle, {"type": "circle", "center": {"x": 1}, "radius": 10})
Traceback (most recent call last):
...
trycast.ValidationError: Expected Circle but found {'type': 'circle', 'center': {'x': 1}, 'radius': 10}
At key 'center': Expected Point2D but found {'x': 1}
Required key 'y' is missing
>>>
ValidationError only spends time generating a message if you try to print it
or stringify it, so can be cheaply caught if you only want to use it for
control flow purposes.
isassignable()
Here is an example of parsing a Shape object defined as a union of
TypedDicts using isassignable():
class Circle(TypedDict):
type: Literal['circle']
...
class Rect(TypedDict):
type: Literal['rect']
...
Shape = Circle | Rect # a Tagged Union!
@route('/draw_shape')
def draw_shape_endpoint() -> HTTPResponse:
request_json = request.json # type: object
if not isassignable(request_json, Shape):
return HTTPResponse(status=400) # Bad Request
draw_shape(request_json) # type is narrowed to Shape
return HTTPResponse(status=200) # OK
Important: Current limitations in the mypy typechecker prevent the automatic narrowing of the type of
request_jsonin the above example toShape, so you must add an additionalcast()to narrow the type manually:if not isassignable(request_json, Shape): ... shape = cast(Shape, request_json) # type is manually narrowed to Shape draw_shape(shape)These limitations are in the process of being resolved by introducing TypeForm support to mypy.
A better isinstance()
isassignable(value, T) is similar to Python's builtin isinstance() but
additionally supports checking against arbitrary type annotation objects
including TypedDicts, Unions, Literals, and many others.
Formally, isassignable(value, T) checks whether value is consistent with a
variable of type T (using PEP 484 static
typechecking rules), but at runtime.
Motivation & Alternatives
Why use trycast?
The trycast module is primarily designed for recognizing JSON-like structures that can be described by Python's typing system. Secondarily, it can be used for recognizing arbitrary structures that can be described by Python's typing system.
Please see Philosophy for more information about how trycast differs from similar libraries like pydantic.
Why use TypedDict?
Typed dictionaries are the natural form that JSON data comes in over the wire. They can be trivially serialized and deserialized without any additional logic. For applications that use a lot of JSON data - such as web applications - using typed dictionaries is very convenient for representing data structures.
If you just need a lightweight class structure that doesn't need excellent support for JSON-serialization you might consider other alternatives for representing data structures in Python such as dataclasses (recommended), named tuples, attrs, or plain classes.
Installation
python -m pip install trycast
Recommendations while using trycast
- So that
trycast()can recognize TypedDicts with mixed required and not-required keys correctly:- Prefer using
typing.TypedDictinstead oftyping_extensions.TypedDict.
- Prefer using
Presentations & Videos
A presentation about using trycast to parse JSON was given at the 2021 PyCon US Typing Summit:
A presentation describing tools that use Python type annotations at runtime, including trycast, was given at the 2022 PyCon US Typing Summit:
Contributing
Pull requests are welcome! The Python Community Code of Conduct does apply.
You can checkout the code locally using:
git clone git@github.com:davidfstr/trycast.git
cd trycast
Create your local virtual environment to develop in using Poetry:
poetry shell
poetry install
You can run the existing automated tests in the current version of Python with:
make test
You can also run the tests against all supported Python versions with:
make testall
See additional development commands by running:
make help
License
Feature Reference
Typing Features Supported
- Scalars
- bool
- int
- float
- None, type(None)
- Strings
- str
- Raw Collections
- list, List
- tuple, Tuple
- Sequence, MutableSequence
- dict, Dict
- Mapping, MutableMapping
- Generic Collections
(including PEP 585)
- list[T], List[T]
- tuple[T, ...], Tuple[T, ...]
- Sequence[T], MutableSequence[T]
- dict[K, V], Dict[K, V]
- Mapping[K, V], MutableMapping[K, V]
- TypedDict
- Tuples (Heterogeneous)
- tuple[T1], tuple[T1, T2], tuple[T1, T2, T3], etc
- Tuple[T1], Tuple[T1, T2], Tuple[T1, T2, T3], etc
- Unions
- Union[X, Y]
- Optional[T]
- X | Y (PEP 604)
- Literals
- Literal[value] (PEP 586)
- Callables
- Callable
- Callable[P, R] (where P=[Any]*N and R=Any)
- NewTypes (when strict=False)
- Special Types
- Any
- Never
- NoReturn
Type Checkers Supported
Trycast does type check successfully with the following type checkers:
- [Mypy]
- [Pyright] / [Pylance]
- [Pyre]
API Reference
<a name="trycast-api"></a>
trycast API
def trycast(
tp: TypeForm[T]† | TypeFormString[T]‡,
value: object,
/, failure: F = None,
*, strict: bool = True,
eval: bool = True
) -> T | F: ...
If value i
Related Skills
node-connect
336.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
82.9kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
82.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
336.5kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.


