Type-Based Parsers
Overview
Valid8r provides automatic parser generation from Python type annotations using the from_type() function.
This enables type-safe parsing with minimal boilerplate, leveraging Python’s type system to automatically
create appropriate parser functions.
The from_type() function uses pattern matching to introspect type annotations and automatically generate
parsers that return Maybe[T] results, maintaining the same error handling philosophy as the rest of Valid8r.
Basic Usage
Simple Types
Generate parsers for basic Python types:
from valid8r.core.type_adapters import from_type
# Integer parser
int_parser = from_type(int)
result = int_parser('42')
# Success(42)
# String parser
str_parser = from_type(str)
result = str_parser('hello')
# Success('hello')
# Float parser
float_parser = from_type(float)
result = float_parser('3.14')
# Success(3.14)
# Boolean parser
bool_parser = from_type(bool)
result = bool_parser('true')
# Success(True)
Optional Types
Handle optional values elegantly with automatic None handling:
from typing import Optional
# Optional integer parser
parser = from_type(Optional[int])
# Parse valid integer
result = parser('42')
# Success(42)
# Empty string becomes None
result = parser('')
# Success(None)
# Invalid input still fails
result = parser('invalid')
# Failure('Expected a valid integer')
Collection Types
Lists
Parse and validate JSON arrays with element type checking:
# List of integers
parser = from_type(list[int])
result = parser('[1, 2, 3, 4, 5]')
# Success([1, 2, 3, 4, 5])
# Validates each element
result = parser('[1, "invalid", 3]')
# Failure('Failed to parse element 2: Expected a valid integer')
# Nested lists
parser = from_type(list[list[int]])
result = parser('[[1, 2], [3, 4]]')
# Success([[1, 2], [3, 4]])
Dictionaries
Parse and validate JSON objects with typed keys and values:
# String keys, integer values
parser = from_type(dict[str, int])
result = parser('{"age": 30, "count": 5}')
# Success({'age': 30, 'count': 5})
# Validates both keys and values
result = parser('{"age": "thirty"}')
# Failure('Failed to parse value for key "age": Expected a valid integer')
# Nested structures
parser = from_type(dict[str, list[int]])
result = parser('{"scores": [95, 87, 92]}')
# Success({'scores': [95, 87, 92]})
Sets
Parse JSON arrays and convert to Python sets:
parser = from_type(set[str])
result = parser('["a", "b", "c", "a"]')
# Success({'a', 'b', 'c'}) # Duplicates removed
Union Types
Try multiple types in order until one succeeds:
from typing import Union
# Try int, then float, then str
parser = from_type(Union[int, float, str])
result = parser('42')
# Success(42) # Parsed as int
result = parser('3.14')
# Success(3.14) # Parsed as float
result = parser('hello')
# Success('hello') # Parsed as str
Literal Types
Restrict values to specific literals:
from typing import Literal
# Color choices
parser = from_type(Literal['red', 'green', 'blue'])
result = parser('red')
# Success('red')
result = parser('yellow')
# Failure('Value must be one of: 'red', 'green', 'blue'')
# Mixed type literals
parser = from_type(Literal[1, 'one', True])
result = parser('1')
# Success(1)
Enum Types
Parse enum values with case-insensitive matching:
from enum import Enum
class Status(Enum):
ACTIVE = 'active'
INACTIVE = 'inactive'
PENDING = 'pending'
parser = from_type(Status)
# Case-insensitive matching
result = parser('ACTIVE')
# Success(Status.ACTIVE)
result = parser('active')
# Success(Status.ACTIVE)
result = parser('invalid')
# Failure('Expected a valid enumeration value')
Annotated Types with Validators
Combine type parsing with validation:
from typing import Annotated
from valid8r import validators
# Integer with range validation
parser = from_type(Annotated[int, validators.minimum(0), validators.maximum(100)])
result = parser('50')
# Success(50)
result = parser('150')
# Failure('Value must be at most 100')
result = parser('-5')
# Failure('Value must be at least 0')
# String with length validation
parser = from_type(Annotated[str, validators.length(3, 10)])
result = parser('hello')
# Success('hello')
result = parser('hi')
# Failure('String must have at least 3 characters')
Advanced Usage
Nested Complex Types
Combine multiple type features:
from typing import Optional, Annotated
# List of optional integers with validation
parser = from_type(list[Optional[Annotated[int, validators.minimum(0)]]])
result = parser('[1, null, 5, 10]')
# Success([1, None, 5, 10])
# Dict with validated values
parser = from_type(dict[str, Annotated[int, validators.minimum(0), validators.maximum(100)]])
result = parser('{"score": 95, "grade": 87}')
# Success({'score': 95, 'grade': 87})
Reusable Type Aliases
Define complex types once and reuse them:
from typing import TypeAlias
# Define reusable types
Score: TypeAlias = Annotated[int, validators.minimum(0), validators.maximum(100)]
Scores: TypeAlias = dict[str, list[Score]]
# Use the alias
parser = from_type(Scores)
result = parser('{"math": [95, 87, 92], "english": [88, 91]}')
# Success({'math': [95, 87, 92], 'english': [88, 91]})
Security Considerations
DoS Protection
All collection parsers include automatic protection against Denial-of-Service (DoS) attacks through input length validation. Inputs exceeding 100KB (100,000 characters) are rejected immediately before expensive JSON parsing:
# This malicious input is rejected in <10ms
malicious_input = '[' + '1,' * 100_000 + '1]'
parser = from_type(list[int])
result = parser(malicious_input)
# Failure('Input too large: maximum 100000 characters')
Input Validation Best Practices
Always validate untrusted input before parsing
Use appropriate type annotations to enforce structure
Add validators to Annotated types for business logic constraints
Consider using Optional for fields that may be absent
Use Union types sparingly - specific types are safer
Limitations
Unsupported Types
The following types are not supported:
Callable- Function types cannot be parsed from stringsTypedDict- Use dataclass integration instead (see dataclasses)Protocol- Structural subtyping not supportedCustom generic types - Only built-in generics supported
Error Messages
Error messages are designed to be user-friendly and indicate the specific problem:
Invalid input:
"Expected a valid integer"Type mismatch:
"Expected a JSON array"Element validation:
"Failed to parse element 3: Expected a valid integer"Size limits:
"Input too large: maximum 100000 characters"
See Also
Parsers - Individual parser functions
Validators - Validation functions for use with Annotated
Schema Validation - Schema-based validation for structured data
Understanding the Maybe Monad - Understanding Maybe[T] results