Taph is a minimalist Python package for enforcing deep, zero-overhead immutability. Taph creates objects that are guaranteed to be unchangeable, enabling predictable, pure functional programming patterns in Python.
Taph's core value proposition: Fast & Reliable Immutability.
- Zero-Overhead: Achieves immutability using Python's Method Resolution Order (MRO) and metaclass injection.
- Memory Efficiency: Enforces
__slots__usage, eliminating the memory footprint of__dict__for every instance. - Deep Immutability: Recursively transforms nested mutable structures (like
list,dict) into immutable counterparts (tuple,MappingProxyType) during class creation. - Zero Dependencies: A single-file core module built using the Python Standard Library.
pip install taphTaph provides two classes of immutable objects:
Immutablefor instantiable data objectsNamespacefor static constants.
Use Immutable for creating value objects (data structures) whose state must never change after initialization.
Contract: Subclasses must define __slots__.
from taph import Immutable, ImmutableError
class Point(Immutable):
__slots__ = ('x', 'y')
def __init__(self, x: int, y: int):
# IMPORTANT: Use super().__setattr__ for initialization!
super().__setattr__('x', x)
super().__setattr__('y', y)
p = Point(10, 20)
# Fails (ImmutableError)
try:
p.x = 30
except ImmutableError as e:
print(f"Success: {e}")
# Fails (ImmutableError)
try:
del p.y
except ImmutableError as e:
print(f"Success: {e}")Use Namespace for static configuration, constants, or utility groups. Namespace classes are non-instantiable and their class attributes are ** frozen** at creation time.
from taph import Namespace, ImmutableError
class AppConfig(Namespace):
__slots__ = () # Required, must be empty
VERSION = "1.0.0"
HOSTS = ["server-a", "server-b"] # Deeply frozen into a tuple
# Access attributes directly
print(f"Version: {AppConfig.VERSION}")
print(f"Hosts Type: {type(AppConfig.HOSTS)}") # <class 'tuple'>
# Fails (ImmutableError) - Cannot modify class attributes
try:
AppConfig.TIMEOUT = 60
except ImmutableError as e:
print(f"Success: {e}")
# Fails (ImmutableError) - Cannot instantiate
try:
_ = AppConfig()
except ImmutableError as e:
print(f"Success: {e}")Taph's freeze utility ensures deep immutability by recursively converting mutable collections during class construction:
| Mutable Type | Taph Equivalent |
|---|---|
list |
tuple |
set |
frozenset |
dict |
types.MappingProxyType |
Custom objects must inherit from Immutable or Namespace, or freeze will raise an ImmutableError at class definition time.
Taph provides a solid foundation for functional programming in Python:
Pass Taph Immutable objects into functions with confidence that no side effects can occur.
from taph import Immutable
class User(Immutable):
__slots__ = ('user_id', 'name', 'is_active')
def __init__(self, user_id: int, name: str, is_active: bool = True):
super().__setattr__('user_id', user_id)
super().__setattr__('name', name)
super().__setattr__('is_active', is_active)
# PURE FUNCTION: No side effects. Takes a User, returns a NEW User.
def deactivate_user(user: User) -> User:
# This is the "copy-on-write" pattern.
return User(
user_id=user.user_id,
name=user.name,
is_active=False
)
# --- Caller ---
user1 = User(101, 'Alice')
user2 = deactivate_user(user1)
# The original object is completely untouched. The system is predictable.
print(user1.is_active) # -> True
print(user2.is_active) # -> False
assert user1 is not user2Use Namespace to provide verifiably safe, global constants that cannot be accidentally mutated by any function.
from taph import Namespace
class Config(Namespace):
__slots__ = ()
TIMEOUT_SECONDS = 30
SUPPORTED_METHODS = ('GET', 'POST')
def is_request_valid(request_duration: int, method: str) -> bool:
# This function is predictable. Its behavior depends on its inputs and
# constants that are guaranteed to be immutable.
if method not in Config.SUPPORTED_METHODS:
return False
return request_duration < Config.TIMEOUT_SECONDS
# Config.TIMEOUT_SECONDS = 1 # Raises ImmutableError, protecting the function.Taph is licensed under the Apache License 2.0. See the LICENSE file for details.