-
Notifications
You must be signed in to change notification settings - Fork 6
Philosophy
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.
The backbone of this recognition is the trycast()
and isassignable()
function-pair which can typecheck against arbitrary type annotation objects at runtime. These functions are intended to be:
-
Consistent with typecheckers -- Checking a value against a type T should be successful iff a type checker such as mypy agrees that a function taking a T parameter would accept the value as an argument. Thus, for example, both int and bool values are considered assignable to float because mypy (and PEP 484) say it should be, even though isinstance() disagrees.
-
Non-coercing -- The "parse" operation performed by
trycast()
will always return the original value upon success and not any other kind of "coerced" or "rounded" value. This is in contrast to libraries like pydantic which may, for example, coerce a str value"5"
to5
when looking for anint
. -
Exhaustively correct -- Checking against a collection type such as
list[str]
will actually look at every element of a list value to ensure that all arestr
s. This is in contrast to libraries like beartype which may probabilistically only check a single element in order to provide faster best-effort checking. -
Decidable -- If you call
trycast(T, value)
for some fixed T, it will either (1) always returnTrue
orFalse
when given avalue
, or (2) always raise an exception (likeTypeNotSupportedException
). -
Standardizable -- Function names use "squishedcase" to align with similar functions in Python's standard library such as
isinstance
,issubclass
, andcast
. Following such a convention allows trycast to potentially be integrated into the officialtyping
module at a later date without looking out-of-place. -
Statically narrowing
-
If
isassignable(value, T)
returns True than a typechecker should be able to automatically narrow the type of value to T. For example:from typing import assert_type # Python 3.11 def parse(x: object): if isassignable(x, str): assert_type(x, str) # type of x is narrowed to str statically func_that_expects_str(x) else: func_that_expects_object(x)
-
The return type of
trycast(T, value)
should be recognized asT | None
by typecheckers. For example:from typing import assert_type # Python 3.11 def parse(x: object): if (cleaned_x := trycast(str, x)) is not None: assert_type(cleaned_x, str) # type of cleaned_x is narrowed to str statically func_that_expects_str(cleaned_x) else: func_that_expects_object(x)
-
Achieving the preceding "static narrowing" goals requires that TypeForm support be added to Python's typing system so that both trycast() and isassignable() can be type-annotated in such a way that typecheckers understand what these functions are doing.
-