Skip to content

Tiny runtime checker for typing annotations with an Option-like API.

License

Notifications You must be signed in to change notification settings

schizza/py-typecheck

Repository files navigation

typecheck-runtime

Tiny runtime checker for typing annotations with an Option-like API.

py-typecheck solves a very specific problem:

I have a value (object or Any) and I want to check if the value refers to specific typing annotation.

Does not parse data. Does not transforms data. Just checks structure and returns refered value or None if validation failed.

Installation

pip install typecheck-runtime

Python ≥ 3.10

Core idea

Main function is checked

from py_typecheck import checked

Acts like Option[T] or Rust-like Some(T, None):

  • returns value if matches the type
  • returns None if validation failed

No exceptions, no side-efects.


Basic usage

value: object = {"a": 1}

if (d := checked(value, dict[str, int])) is not None:
    print(d["a"] + 1)

Important

  • always compare against None when using checked
  • do not rely on truthiness (0, False, [] are valid values!)
  • bool is treated as not matching int (so True wont pass as int)

checked_or

If you want the same runtime check as checked(), but with a convenient fallback, use checked_or().

  • returns the original value if it matches the target type
  • otherwise returns the provided default

This is especially handy when you want a safe, typed value in one expression without handling None.

from py_typecheck import checked_or

age: object = "not-a-number"

# returns 10 because "not-a-number" is not an int
value = checked_or(age, int, 10)

Works with typing constructs the same way as checked():

from py_typecheck import checked_or

payload: object = {"tags": ["a", "b"]}

tags = checked_or(payload, dict[str, list[str]], {"tags": []})["tags"]

Important notes:

  • checked_or() relies on checked() semantics (including the bool vs int rule)
  • default should already be the correct value you want to use (no coercion happens)

Supported typing constructs

py-typecheck supports common runtime-validable constructs from typing:

Primitive types

checked(1, int)          # 1
checked("x", int)        # None
checked(True, int)       # None

Note: bool is not considered an int in this library.

Union (X | Y, typing.Union)

checked(1, int | str)      # 1
checked("x", int | str)    # "x"
checked(1.5, int | str)    # None

List / Set / FrozenSet

checked([1, 2], list[int])           # [1, 2]
checked({1, 2}, set[int])            # {1, 2}
checked(frozenset({1}), frozenset[int])

Nested structures work as expected:

checked([[1, 2], [3]], list[list[int]])

Dict

checked({"a": 1}, dict[str, int])
checked({"a": "x"}, dict[str, int])   # None

Supports unions and nesting:

checked({"a": [1, 2]}, dict[str, list[int]])

Tuple

Fixed-length:

checked((1, "x"), tuple[int, str])

Variadic:

checked((1, 2, 3), tuple[int, ...])

Literal

from typing import Literal

checked(True, Literal[True])    # True
checked(False, Literal[True])   # None

Any

from typing import Any

checked(object(), Any)   # always matches

is_type

There is also a boolean helper:

from py_typecheck import is_type

if is_type(value, dict[str, int]):
    ...
  • It returns only True / False.
  • For most code paths, checked is preferred, because it avoids type confusion and double-checking mistakes.

Design principles

  • Explicit is better than clever
  • No exceptions for control flow
  • No implicit casting
  • No data mutation
  • No dependency on dataclasses or models
  • One function, one responsibility

This is not a validator framework. This is a runtime structural check with a safe return value.

Non-goals

py-typecheck intentionally does not:

  • coerce types ("1" → 1)
  • fill defaults
  • validate constraints (ranges, regexes, etc.)
  • replace pydantic or attrs

If you want parsing + validation + coercion → use Pydantic. If you want “is this already the right shape?” → use this.


Comparison

Tool Purpose
isinstance Runtime type only
typing Static type checking
pydantic Parsing + validation + coercion
py-typecheck Runtime structural check only

Development status

  • Fully typed (basedpyright strict)
  • Tested against Python 3.10 – 3.13
  • CI + lint + coverage
  • Small surface area, easy to audit

License MIT

About

Tiny runtime checker for typing annotations with an Option-like API.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages