research: beartype runtime type checking#

date: 2025-12-19 question: investigate beartype for runtime type checking, determine how to integrate into plyr.fm

summary#

beartype is a runtime type checker that validates Python type hints at execution time with O(1) worst-case performance. it's already a transitive dependency via py-key-value-aio. FastMCP does not use beartype. integration would require adding beartype_this_package() to backend/src/backend/__init__.py.

findings#

what beartype does#

  • validates type hints at runtime when functions are called
  • O(1) non-amortized worst-case time (constant time regardless of data structure size)
  • zero runtime dependencies, pure Python
  • MIT license

key integration patterns#

package-wide (recommended):

# At the very top of backend/src/backend/__init__.py
from beartype.claw import beartype_this_package
beartype_this_package()  # enables type-checking for all submodules

per-function:

from beartype import beartype

@beartype
def my_function(x: int) -> str:
    return str(x)

configuration options (BeartypeConf)#

key parameters:

  • violation_type - exception class to raise (default: BeartypeCallHintViolation)
  • violation_param_type - exception for parameter violations
  • violation_return_type - exception for return type violations
  • strategy - checking strategy (default: O1 for O(1) time)
  • is_debug - enable debugging output
  • claw_skip_package_names - packages to exclude from type checking

example with warnings for third-party code:

from beartype import BeartypeConf
from beartype.claw import beartype_all, beartype_this_package

beartype_this_package()  # strict for our code
beartype_all(conf=BeartypeConf(violation_type=UserWarning))  # warn for third-party

current state in plyr.fm#

beartype is already installed as a transitive dependency:

  • backend/uv.lock:477-482 - beartype 0.22.8 present
  • pulled in by py-key-value-aio and py-key-value-shared

FastMCP status#

FastMCP does not use beartype:

  • not in FastMCP's dependencies
  • FastMCP uses type hints for schema generation/documentation, not runtime validation

integration approach for plyr.fm#

  1. add explicit dependency (optional but good for clarity):

    # pyproject.toml
    dependencies = [
        "beartype>=0.22.0",
        # ... existing deps
    ]
    
  2. enable in __init__.py:

    # backend/src/backend/__init__.py
    from beartype.claw import beartype_this_package
    beartype_this_package()
    
    def hello() -> str:
        return "Hello from backend!"
    
  3. considerations:

    • must be called before importing any submodules
    • main.py currently imports warnings before filtering, then imports submodules
    • beartype should be activated in __init__.py, not main.py

potential concerns#

  1. performance: O(1) guarantees should be fine, but worth benchmarking
  2. third-party compatibility: some libraries may have inaccurate type hints; use claw_skip_package_names or warn mode
  3. FastAPI: pydantic already validates request/response types; beartype adds internal function validation

code references#

  • backend/uv.lock:477-482 - beartype 0.22.8 in lockfile
  • backend/uv.lock:2240 - py-key-value-aio depends on beartype
  • backend/uv.lock:2261 - py-key-value-shared depends on beartype
  • backend/src/backend/__init__.py:1-2 - current init (needs modification)
  • backend/src/backend/main.py:1-50 - app initialization (imports after warnings filter)

open questions#

  • should we enable strict mode (exceptions) or warning mode initially?
  • which third-party packages might have problematic type hints to skip?
  • should we benchmark API response times before/after enabling?