Persistent/Functional/Immutable data structures for Python
—
Runtime type validation for persistent collections with optional invariant checking. These collections provide the same functionality as core collections but with automatic type checking and validation.
Persistent map with runtime validation of key and value types. Raises CheckedKeyTypeError or CheckedValueTypeError when invalid types are used.
class CheckedPMap(PMap):
"""
Type-checked persistent map with key and value type validation.
Class attributes:
- __key_type__: Required key type or tuple of allowed types
- __value_type__: Required value type or tuple of allowed types
"""
__key_type__: type
__value_type__: type
def __new__(cls, source: Mapping = {}, size: int = 0) -> 'CheckedPMap': ...
@classmethod
def create(cls, source_data: Mapping, _factory_fields=None) -> 'CheckedPMap':
"""Create instance with type validation."""
def serialize(self, format=None) -> dict:
"""Serialize to regular dict."""Persistent vector with runtime validation of element types.
class CheckedPVector(PVector):
"""
Type-checked persistent vector with element type validation.
Class attributes:
- __type__: Required element type or tuple of allowed types
"""
__type__: type
def __new__(cls, initial: Iterable = ()) -> 'CheckedPVector': ...
@classmethod
def create(cls, source_data: Iterable, _factory_fields=None) -> 'CheckedPVector':
"""Create instance with type validation."""
def serialize(self, format=None) -> list:
"""Serialize to regular list."""Persistent set with runtime validation of element types.
class CheckedPSet(PSet):
"""
Type-checked persistent set with element type validation.
Class attributes:
- __type__: Required element type or tuple of allowed types
"""
__type__: type
def __new__(cls, initial: Iterable = ()) -> 'CheckedPSet': ...
@classmethod
def create(cls, source_data: Iterable, _factory_fields=None) -> 'CheckedPSet':
"""Create instance with type validation."""
def serialize(self, format=None) -> set:
"""Serialize to regular set."""Abstract base class for all type-checked collections providing common validation infrastructure.
class CheckedType:
"""Abstract base class for type-checked collections."""
@classmethod
def create(cls, source_data, _factory_fields=None):
"""Factory method for creating instances with validation."""
def serialize(self, format=None):
"""Serialize to corresponding Python built-in type."""Allow a field or collection to accept specified types or None.
def optional(*types) -> tuple:
"""
Create a type specification that allows specified types or None.
Parameters:
- *types: Types to allow (in addition to None)
Returns:
Tuple of types including None
Example:
optional(int, str) -> (int, str, type(None))
"""Type checking exceptions raised when validation fails:
class InvariantException(Exception):
"""
Raised when invariant validation fails.
Attributes:
- invariant_errors: Tuple of validation error messages
- missing_fields: Tuple of missing required field names
"""
invariant_errors: tuple
missing_fields: tuple
class CheckedTypeError(TypeError):
"""
Base exception for type validation errors in checked collections.
Attributes:
- source_class: Class that raised the error
- expected_types: Tuple of expected types
- actual_type: Actual type that was provided
- actual_value: The value that caused the error
"""
source_class: type
expected_types: tuple
actual_type: type
actual_value: object
class CheckedKeyTypeError(CheckedTypeError):
"""Raised when CheckedPMap receives invalid key type."""
class CheckedValueTypeError(CheckedTypeError):
"""Raised when CheckedPMap/CheckedPVector/CheckedPSet receives invalid value/element type."""from pyrsistent import CheckedPMap, CheckedPVector, CheckedPSet, optional
# Define a type-checked map for string keys and integer values
class StringIntMap(CheckedPMap):
__key_type__ = str
__value_type__ = int
# Create and use the map
sim = StringIntMap({'a': 1, 'b': 2})
sim2 = sim.set('c', 3) # OK
# sim.set(123, 4) # Raises CheckedKeyTypeError
# sim.set('d', 'invalid') # Raises CheckedValueTypeError
# Define a type-checked vector for integers
class IntVector(CheckedPVector):
__type__ = int
iv = IntVector([1, 2, 3])
iv2 = iv.append(4) # OK
# iv.append('invalid') # Raises CheckedValueTypeError
# Define a type-checked set with optional types
class MixedSet(CheckedPSet):
__type__ = optional(int, str) # Allows int, str, or None
ms = MixedSet([1, 'hello', None, 2]) # OK
# ms.add(3.14) # Raises CheckedValueTypeError# Type-checked collections can include custom validation
class PositiveIntVector(CheckedPVector):
__type__ = int
def __new__(cls, initial=()):
# Custom validation in constructor
for item in initial:
if not isinstance(item, int) or item <= 0:
raise ValueError("All elements must be positive integers")
return super().__new__(cls, initial)
piv = PositiveIntVector([1, 2, 3]) # OK
# PositiveIntVector([1, -2, 3]) # Raises ValueError# Type-checked collections can be serialized to regular Python types
class PersonMap(CheckedPMap):
__key_type__ = str
__value_type__ = (str, int)
pm = PersonMap({'name': 'Alice', 'age': 30})
regular_dict = pm.serialize() # Returns {'name': 'Alice', 'age': 30}
print(type(regular_dict)) # <class 'dict'># Use create() class method for explicit construction with validation
data = {'valid_key': 42}
validated_map = StringIntMap.create(data)
# Validation happens during creation
try:
invalid_data = {123: 'invalid'}
StringIntMap.create(invalid_data)
except CheckedKeyTypeError as e:
print(f"Key type error: {e}")from pyrsistent import CheckedKeyTypeError, CheckedValueTypeError
class TypedData(CheckedPMap):
__key_type__ = str
__value_type__ = (int, float)
try:
td = TypedData()
td = td.set(42, 'invalid') # Wrong key type
except CheckedKeyTypeError as e:
print(f"Expected key types: {e.expected_types}")
print(f"Actual key type: {e.actual_type}")
print(f"Actual key value: {e.actual_value}")
try:
td = TypedData()
td = td.set('key', 'invalid') # Wrong value type
except CheckedValueTypeError as e:
print(f"Expected value types: {e.expected_types}")
print(f"Actual value type: {e.actual_type}")
print(f"Actual value: {e.actual_value}")Install with Tessl CLI
npx tessl i tessl/pypi-pyrsistent