Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Data validation beyond basic type checking using composable validator factories. The beartype.vale module provides a hierarchy of subscriptable classes for creating custom validation logic that integrates seamlessly with beartype's type checking system.
Create validators from arbitrary callable predicates.
class Is:
"""
Generic validator factory for callable predicates.
Creates validators from functions that return boolean values.
Used with typing.Annotated to add validation logic to type hints.
"""
def __class_getitem__(cls, predicate):
"""
Create validator from predicate function.
Parameters:
- predicate: callable - Function that takes object and returns bool
Returns:
BeartypeValidator - Validator object for use with Annotated
"""Usage examples:
from beartype import beartype
from beartype.vale import Is
from typing import Annotated
# Positive integer validator
PositiveInt = Annotated[int, Is[lambda x: x > 0]]
@beartype
def deposit(amount: PositiveInt) -> str:
return f"Deposited ${amount}"
deposit(100) # ✓ Valid
# deposit(-50) # ✗ Raises validation error
# String length validator
NonEmptyStr = Annotated[str, Is[lambda s: len(s) > 0]]
@beartype
def greet(name: NonEmptyStr) -> str:
return f"Hello, {name}!"
greet("Alice") # ✓ Valid
# greet("") # ✗ Raises validation error
# Complex validation logic
def is_valid_email(email: str) -> bool:
return "@" in email and "." in email.split("@")[-1]
Email = Annotated[str, Is[is_valid_email]]
@beartype
def send_email(address: Email, message: str) -> bool:
print(f"Sending to {address}: {message}")
return TrueCreate validators based on object attributes.
class IsAttr:
"""
Attribute-based validator factory.
Creates validators that check object attributes against predicates.
"""
def __class_getitem__(cls, params):
"""
Create attribute validator.
Parameters:
- params: str or tuple - Attribute name or (attr_name, predicate)
Returns:
BeartypeValidator - Validator for object attributes
"""Usage examples:
from beartype.vale import IsAttr
from typing import Annotated
from dataclasses import dataclass
# Check for attribute existence
HasName = Annotated[object, IsAttr["name"]]
# Check attribute value
HasPositiveLength = Annotated[object, IsAttr[("__len__", lambda x: x > 0)]]
@dataclass
class Person:
name: str
age: int
@beartype
def process_person(person: HasName) -> str:
return f"Processing {person.name}"
person = Person("Alice", 30)
process_person(person) # ✓ Valid
# List length validation
@beartype
def process_items(items: HasPositiveLength) -> int:
return len(items)
process_items([1, 2, 3]) # ✓ Valid
# process_items([]) # ✗ Raises validation errorCreate validators based on equality comparisons.
class IsEqual:
"""
Equality-based validator factory.
Creates validators that check equality against specific values.
"""
def __class_getitem__(cls, value):
"""
Create equality validator.
Parameters:
- value: Any - Value to compare against
Returns:
BeartypeValidator - Validator for equality checking
"""Usage examples:
from beartype.vale import IsEqual
from typing import Annotated, Literal
# Exact value validation
StatusOK = Annotated[int, IsEqual[200]]
StatusError = Annotated[int, IsEqual[404]]
ConfigMode = Annotated[str, IsEqual["production"]]
@beartype
def handle_response(status: StatusOK) -> str:
return "Request successful"
@beartype
def set_config(mode: ConfigMode) -> None:
print(f"Setting config to {mode}")
handle_response(200) # ✓ Valid
# handle_response(404) # ✗ Raises validation error
set_config("production") # ✓ Valid
# set_config("development") # ✗ Raises validation errorCreate validators based on isinstance() checks.
class IsInstance:
"""
Instance type validator factory.
Creates validators using isinstance() checks against type or tuple of types.
"""
def __class_getitem__(cls, types):
"""
Create isinstance validator.
Parameters:
- types: type or tuple - Type(s) for isinstance check
Returns:
BeartypeValidator - Validator using isinstance()
"""Usage examples:
from beartype.vale import IsInstance
from typing import Annotated, Union
import datetime
# Single type validation
NumericValue = Annotated[Union[int, float], IsInstance[int, float]]
# Multiple type validation
DateTimeValue = Annotated[object, IsInstance[(datetime.date, datetime.datetime)]]
@beartype
def calculate(value: NumericValue) -> float:
return float(value) * 2.0
@beartype
def format_date(dt: DateTimeValue) -> str:
return dt.strftime("%Y-%m-%d")
calculate(42) # ✓ Valid (int)
calculate(3.14) # ✓ Valid (float)
# calculate("42") # ✗ Raises validation error
format_date(datetime.date.today()) # ✓ Valid
format_date(datetime.datetime.now()) # ✓ Valid
# format_date("2023-01-01") # ✗ Raises validation errorCreate validators based on issubclass() checks.
class IsSubclass:
"""
Subclass validator factory.
Creates validators using issubclass() checks against type or tuple of types.
"""
def __class_getitem__(cls, types):
"""
Create issubclass validator.
Parameters:
- types: type or tuple - Type(s) for issubclass check
Returns:
BeartypeValidator - Validator using issubclass()
"""Usage examples:
from beartype.vale import IsSubclass
from typing import Annotated
from abc import ABC, abstractmethod
# Abstract base class
class Drawable(ABC):
@abstractmethod
def draw(self): pass
class Shape(Drawable):
def draw(self): return "Drawing shape"
class Circle(Shape):
def draw(self): return "Drawing circle"
# Validator for subclasses
DrawableClass = Annotated[type, IsSubclass[Drawable]]
ShapeClass = Annotated[type, IsSubclass[Shape]]
@beartype
def create_drawable(cls: DrawableClass) -> Drawable:
return cls()
@beartype
def create_shape(cls: ShapeClass) -> Shape:
return cls()
create_drawable(Circle) # ✓ Valid
create_shape(Circle) # ✓ Valid
# create_drawable(str) # ✗ Raises validation errorMultiple validators can be combined using multiple annotations:
from beartype.vale import Is, IsAttr
from typing import Annotated
# Multiple validation constraints
ValidUser = Annotated[
object,
IsAttr["name"], # Must have name attribute
IsAttr[("age", lambda x: x >= 18)], # Age must be >= 18
Is[lambda obj: len(obj.name) > 0] # Name must be non-empty
]
@dataclass
class User:
name: str
age: int
@beartype
def register_user(user: ValidUser) -> str:
return f"Registered {user.name}, age {user.age}"
valid_user = User("Alice", 25)
register_user(valid_user) # ✓ Valid
invalid_user = User("", 16)
# register_user(invalid_user) # ✗ Raises validation errorfrom beartype.vale import Is
from typing import Annotated, Union
def validate_id(obj) -> bool:
if isinstance(obj, str):
return obj.isalnum() and len(obj) >= 3
elif isinstance(obj, int):
return obj > 0
return False
FlexibleID = Annotated[Union[str, int], Is[validate_id]]
@beartype
def lookup_item(item_id: FlexibleID) -> str:
return f"Item {item_id}"
lookup_item("abc123") # ✓ Valid string ID
lookup_item(42) # ✓ Valid numeric ID
# lookup_item("ab") # ✗ Too short
# lookup_item(-1) # ✗ Negative numberfrom beartype.vale import Is
from typing import Annotated
def positive_with_message(x: int) -> bool:
if x <= 0:
raise ValueError(f"Expected positive integer, got {x}")
return True
PositiveInt = Annotated[int, Is[positive_with_message]]
@beartype
def process_positive(value: PositiveInt) -> int:
return value * 2
try:
process_positive(-5)
except Exception as e:
print(f"Validation failed: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-beartype