ProvenPin Install

Runtime type checking with typeguard: @typechecked, check_type and generics: working versions (tested 2026-10-18)

Enforce annotations at runtime with typeguard: @typechecked functions, standalone check_type, TypeCheckError messages and bounded TypeVars, plus the FIRST_ITEM container gotcha.

passed6 passed · 0 failed2026-10-18 03:57 UTC
Status
passed
Last passed
2026-10-18 03:57 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions2
typeguard@4.6.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 39095370682

The code

Exactly the files that passed. Your agent can pull them with get_recipe("typeguard-runtime-typecheck") (install), or see them on GitHub.

requirements.txt
typeguard==4.6.0
pytest==9.1.1
checks.py
from collections.abc import Sequence
from numbers import Number
from typing import TypeVar

from typeguard import CollectionCheckStrategy, check_type, typechecked

T = TypeVar("T", bound=Number)
U = TypeVar("U")


@typechecked
def clamp(value: int, low: int, high: int) -> int:
    return max(low, min(high, value))


@typechecked
def mean(values: Sequence[float]) -> float:
    return sum(values) / len(values)


@typechecked
def checked_sum(a: T, b: T) -> T:
    return a + b


@typechecked
def pair(a: U, b: U) -> tuple[U, U]:
    return a, b


def parse_ids(raw: object) -> list[int]:
    return check_type(
        raw, list[int], collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS
    )
tests/test_checks.py
import pytest
from typeguard import CollectionCheckStrategy, TypeCheckError, check_type, typechecked

from checks import checked_sum, clamp, mean, pair, parse_ids


def test_argument_types_are_checked():
    assert clamp(5, 0, 10) == 5
    with pytest.raises(
        TypeCheckError, match=r'argument "value" \(str\) is not an instance of int'
    ):
        clamp("5", 0, 10)


def test_return_value_is_checked():
    @typechecked
    def bad() -> int:
        return "nope"  # type: ignore[return-value]

    with pytest.raises(
        TypeCheckError, match=r"the return value \(str\) is not an instance of int"
    ):
        bad()


def test_check_type_returns_value_and_raises():
    assert check_type(3, int) == 3
    with pytest.raises(TypeCheckError, match="str is not an instance of int"):
        check_type("3", int)


def test_container_check_is_first_item_by_default():
    # the default strategy only inspects element 0, so a bad tail slips through
    assert check_type([1, "a"], list[int]) == [1, "a"]
    with pytest.raises(TypeCheckError, match="item 1 of list is not an instance of int"):
        check_type(
            [1, "a"],
            list[int],
            collection_check_strategy=CollectionCheckStrategy.ALL_ITEMS,
        )
    assert parse_ids([1, 2]) == [1, 2]
    with pytest.raises(TypeCheckError, match="item 1 of list is not an instance of int"):
        parse_ids([1, "2"])


def test_sequence_accepts_numeric_tower():
    assert mean((1, 2, 3)) == 2.0
    with pytest.raises(
        TypeCheckError,
        match=r'item 0 of argument "values" \(str\) is neither float or int',
    ):
        mean("abc")


def test_only_bounded_typevars_are_enforced():
    assert checked_sum(1, 2.5) == 3.5
    with pytest.raises(
        TypeCheckError,
        match=r'argument "b" \(str\) is not an instance of numbers.Number',
    ):
        checked_sum(1, "a")
    # an unbounded TypeVar is treated as Any: no cross argument unification
    assert pair(1, "a") == (1, "a")
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.