API

Package frigid

Data structures which are completely immutable after creation. This behavior is useful for configuration objects, value objects, and other scenarios requiring collections with strong immutability guarantees.

  • Dictionary: A dict-like structure that becomes completely immutable after creation. Includes a ValidatorDictionary variant, which validates entries during creation.

  • Namespace: Similar to types.SimpleNamespace but completely immutable after creation.

  • Module: A module type that enforces complete attribute immutability.

  • Object: Base class for objects with immutable attributes.

  • Class: Metaclass for creating classes with immutable class attributes.

  • immutable: Decorator for making classes produce immutable instances.

Module frigid.dictionaries

Immutable dictionaries.

Dictionaries which cannot be modified after creation.

Note

While types.MappingProxyType also provides a read-only view of a dictionary, it has important differences from Dictionary:

  • A MappingProxyType is a view over a mutable dictionary, so its contents can still change if the underlying dictionary is modified.

  • Dictionary owns its data and guarantees that it will never change.

  • Dictionary provides set operations (union, intersection) that maintain immutability guarantees.

Use MappingProxyType when you want to expose a read-only view of a dictionary that might need to change. Use Dictionary when you want to ensure that the data can never change, such as for configuration objects or other cases requiring strong immutability guarantees.

  • AbstractDictionary: Base class defining the immutable dictionary interface. Implementations must provide __getitem__, __iter__, and __len__.

  • Dictionary: Standard implementation of an immutable dictionary. Supports all usual dict read operations but prevents any modifications.

  • ValidatorDictionary: Validates entries before addition using a supplied predicate function.

>>> from frigid import Dictionary
>>> d = Dictionary( x = 1, y = 2 )
>>> d[ 'z' ] = 3  # Attempt to add entry
Traceback (most recent call last):
    ...
frigid.exceptions.EntryImmutabilityError: Cannot assign or delete entry for 'z'.
>>> d[ 'x' ] = 4  # Attempt modification
Traceback (most recent call last):
    ...
frigid.exceptions.EntryImmutabilityError: Cannot assign or delete entry for 'x'.
>>> del d[ 'y' ]  # Attempt removal
Traceback (most recent call last):
    ...
frigid.exceptions.EntryImmutabilityError: Cannot assign or delete entry for 'y'.
class frigid.dictionaries.AbstractDictionary

Bases: Mapping[H, V]

Abstract base class for immutable dictionaries.

An immutable dictionary prevents modification or removal of entries after creation. This provides a clean interface for dictionaries that should never change.

Implementations must provide: - __getitem__, __iter__, __len__

get(k[, d]) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
values() an object providing a view on D's values
class frigid.dictionaries.Dictionary(*iterables: Annotated[Mapping[H, V] | Iterable[tuple[H, V]], Doc('Zero or more iterables from which to initialize dictionary data. Each iterable must be dictionary or sequence of key-value pairs. Duplicate keys will result in an error.')], **entries: Annotated[V, Doc('Zero or more keyword arguments from which to initialize dictionary data.')])

Bases: Object, _DictionaryOperations[H, V]

Immutable dictionary.

Prevents addition, alteration, or removal of dictionary entries after creation.

copy() Self

Provides fresh copy of dictionary.

get(key: H, default: V | AbsentSingleton = absence.absent) Annotated[V, Doc('Value of entry, if it exists. Else, supplied default value or ``None``.')]

Retrieves entry associated with key, if it exists.

items() ItemsView[H, V]

Provides iterable view over dictionary items.

keys() KeysView[H]

Provides iterable view over dictionary keys.

values() ValuesView[V]

Provides iterable view over dictionary values.

with_data(*iterables: Annotated[Mapping[H, V] | Iterable[tuple[H, V]], Doc('Zero or more iterables from which to initialize dictionary data. Each iterable must be dictionary or sequence of key-value pairs. Duplicate keys will result in an error.')], **entries: Annotated[V, Doc('Zero or more keyword arguments from which to initialize dictionary data.')]) Self

Creates new dictionary with same behavior but different data.

class frigid.dictionaries.ValidatorDictionary(validator: Annotated[Callable[[H, V], bool], Doc('Callable which validates entries before addition to dictionary.')], /, *iterables: Annotated[Mapping[H, V] | Iterable[tuple[H, V]], Doc('Zero or more iterables from which to initialize dictionary data. Each iterable must be dictionary or sequence of key-value pairs. Duplicate keys will result in an error.')], **entries: Annotated[V, Doc('Zero or more keyword arguments from which to initialize dictionary data.')])

Bases: Dictionary[H, V]

Immutable dictionary with validation of entries on initialization.

Prevents addition, alteration, or removal of dictionary entries after creation.

When an attempt to create a dictionary with entries, each entry is validated against supplied criteria. If validation fails for any entry, then the dictionary creation is rejected.

copy() Self

Provides fresh copy of dictionary.

get(key: H, default: V | AbsentSingleton = absence.absent) Annotated[V, Doc('Value of entry, if it exists. Else, supplied default value or ``None``.')]

Retrieves entry associated with key, if it exists.

items() ItemsView[H, V]

Provides iterable view over dictionary items.

keys() KeysView[H]

Provides iterable view over dictionary keys.

values() ValuesView[V]

Provides iterable view over dictionary values.

with_data(*iterables: Annotated[Mapping[H, V] | Iterable[tuple[H, V]], Doc('Zero or more iterables from which to initialize dictionary data. Each iterable must be dictionary or sequence of key-value pairs. Duplicate keys will result in an error.')], **entries: Annotated[V, Doc('Zero or more keyword arguments from which to initialize dictionary data.')]) Self

Creates new dictionary with same behavior but different data.

Module frigid.namespaces

Immutable namespaces.

Provides a namespace type with immutable attributes. Similar to types.SimpleNamespace, but attributes cannot be modified or deleted after initialization.

The namespace implementation is modeled after types.SimpleNamespace but adds immutability. Like types.SimpleNamespace, it provides a simple __repr__ which lists all attributes.

>>> from frigid import Namespace
>>> ns = Namespace( x = 1, y = 2 )
>>> ns.z = 3  # Attempt to add attribute
Traceback (most recent call last):
    ...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'z'.
>>> ns.x = 4  # Attempt modification
Traceback (most recent call last):
    ...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'x'.
>>> ns
frigid.namespaces.Namespace( x = 1, y = 2 )
class frigid.namespaces.Namespace(*iterables: Annotated[Mapping[H, V] | Iterable[tuple[H, V]], Doc('Zero or more iterables from which to initialize dictionary data. Each iterable must be dictionary or sequence of key-value pairs. Duplicate keys will result in an error.')], **attributes: Annotated[V, Doc('Zero or more keyword arguments from which to initialize dictionary data.')])

Bases: Object

Immutable namespaces.

A namespace is an object, whose attributes can be determined from iterables and keyword arguments, at initialization time. The string representation of the namespace object reflects its current instance attributes. Modeled after types.SimpleNamespace.

Prevents assignment or deletion of instance attributes after instance creation.

Module frigid.modules

Immutable modules.

Provides a module type that enforces complete attribute immutability. This helps ensure that module-level constants remain constant and that module interfaces remain stable during runtime.

The module implementation is derived from types.ModuleType and adds immutability. This makes it particularly useful for:

  • Ensuring constants remain constant

  • Preventing modification of module interfaces

Also provides a convenience function:

  • reclassify_modules: Converts existing modules to immutable modules.

class frigid.modules.Module(name, doc=None)

Bases: ModuleType

Immutable modules.

Derived from types.ModuleType, this class is suitable for use as a Python module class.

Prevents assignment or deletion of module attributes after module creation.

This behavior helps ensure that module-level constants remain constant and that module interfaces remain stable during runtime.

frigid.modules.reclassify_modules(attributes: Annotated[Mapping[str, Any] | ModuleType | str, Doc('Module, module name, or dictionary of object attributes.')], recursive: Annotated[bool, Doc('Recursively reclassify package modules?')] = False) None

Reclassifies modules to be immutable.

This function converts existing modules to immutable modules, enforcing attribute immutability after conversion. It can operate on individual modules or entire package hierarchies.

Notes

  • Only converts modules within the same package to prevent unintended modifications to external modules

  • When used with a dictionary, converts any module objects found as values if they belong to the same package

  • Module conversion is permanent for the runtime session

  • Has no effect on already-immutable modules

Module frigid.classes

Immutable classes.

Provides metaclasses for creating classes with immutable attributes. Once a class is initialized, its attributes cannot be reassigned or deleted.

The implementation includes:

  • Class: Standard metaclass for immutable classes; derived from type.

  • ABCFactory: Metaclass for abstract base classes; derived from abc.ABCMeta.

  • ProtocolClass: Metaclass for protocol classes; derived from typing.Protocol.

These metaclasses are particularly useful for:

  • Creating classes with constant class attributes

  • Defining stable abstract base classes

  • Building protocol classes with fixed interfaces

>>> from frigid import Class
>>> class Example( metaclass = Class ):
...     x = 1
>>> Example.y = 2  # Attempt assignment
Traceback (most recent call last):
    ...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'y'.
>>> Example.x = 3  # Attempt reassignment
Traceback (most recent call last):
    ...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'x'.
class frigid.classes.ABCFactory(name: str, bases: tuple[type, ...], namespace: dict[str, Any], *, decorators: Iterable[Callable[[type], type]] = (), docstring: str | None | AbsentSingleton = absence.absent, mutables: Collection[str] = (), **args: Any)

Bases: ABCMeta

Immutable abstract base class factory.

Derived from type, this is a metaclass. A metaclass is a class factory class. I.e., it is a class that produces other classes as its instances.

Prevents assignment or deletion of class attributes after class creation.

mro()

Return a type’s method resolution order.

register(subclass)

Register a virtual subclass of an ABC.

Returns the subclass, to allow usage as a class decorator.

class frigid.classes.Class(name: str, bases: tuple[type, ...], namespace: dict[str, Any], *, decorators: Iterable[Callable[[type], type]] = (), docstring: str | None | AbsentSingleton = absence.absent, mutables: Collection[str] = (), **args: Any)

Bases: type

Immutable class factory.

Derived from type, this is a metaclass. A metaclass is a class factory class. I.e., it is a class that produces other classes as its instances.

Prevents assignment or deletion of class attributes after class creation.

mro()

Return a type’s method resolution order.

frigid.classes.ClassDecorators

alias of Iterable[Callable[[type], type]]

class frigid.classes.ProtocolClass(name: str, bases: tuple[type, ...], namespace: dict[str, Any], *, decorators: Iterable[Callable[[type], type]] = (), docstring: str | None | AbsentSingleton = absence.absent, mutables: Collection[str] = (), **args: Any)

Bases: _ProtocolMeta

Immutable protocol class factory.

Derived from type, this is a metaclass. A metaclass is a class factory class. I.e., it is a class that produces other classes as its instances.

Prevents assignment or deletion of class attributes after class creation.

mro()

Return a type’s method resolution order.

register(subclass)

Register a virtual subclass of an ABC.

Returns the subclass, to allow usage as a class decorator.

Module frigid.objects

Immutable objects.

Provides a base class and decorator for creating objects with immutable attributes. Once an object is initialized, its attributes cannot be modified or deleted.

The implementation uses a special dictionary type for attribute storage that enforces immutability. This makes it suitable for:

  • Configuration objects

  • Value objects

  • Immutable data containers

  • Objects requiring attribute stability

>>> from frigid import Object
>>> class Point( Object ):
...     def __init__( self, x, y ):
...         self.x = x
...         self.y = y
...         super( ).__init__( )
...
>>> obj = Point( 1, 2 )  # Initialize with attributes
>>> obj.z = 3  # Attempt to add attribute
Traceback (most recent call last):
...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'z'.
>>> obj.x = 4  # Attempt modification
Traceback (most recent call last):
...
frigid.exceptions.AttributeImmutabilityError: Cannot assign or delete attribute 'x'.
class frigid.objects.Object(*posargs: Any, **nomargs: Any)

Bases: object

Immutable objects.

Prevents assignment or deletion of instance attributes after instance creation.

frigid.objects.immutable(class_: type[C]) type[C]

Decorator which makes class immutable after initialization.

Cannot be applied to classes which define their own __setattr__ or __delattr__ methods.

Module frigid.exceptions

Family of exceptions for package API.

Provides a hierarchy of exceptions that are raised when immutability is violated. The hierarchy is designed to allow both specific and general exception handling.

  • Omniexception: Base for all package exceptions

  • Omnierror: Base for all package errors

  • AttributeImmutabilityError: Raised for attribute modification

  • EntryImmutabilityError: Raised for dictionary entry modification

exception frigid.exceptions.AttributeImmutabilityError(name: str)

Bases: Omnierror, AttributeError, TypeError

Attempt to modify immutable attribute.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
name

attribute name

obj

object

exception frigid.exceptions.DecoratorCompatibilityError(class_name: str, method_name: str)

Bases: Omnierror, TypeError

Attempt to apply decorator to incompatible class.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
exception frigid.exceptions.EntryImmutabilityError(key: Hashable)

Bases: Omnierror, TypeError

Attempt to modify immutable dictionary entry.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
exception frigid.exceptions.EntryValidityError(indicator: Hashable, value: Any)

Bases: Omnierror, ValueError

Attempt to add invalid entry to dictionary.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
exception frigid.exceptions.Omnierror

Bases: Omniexception, Exception

Base for error exceptions raised by package API.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
exception frigid.exceptions.Omniexception

Bases: ImmutableObject, BaseException

Base for all exceptions raised by package API.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args
exception frigid.exceptions.OperationInvalidity(name: str)

Bases: Omnierror, RuntimeError, TypeError

Attempt to perform invalid operation.

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

args

Module frigid.qaliases

Qualified aliases to immutable data structures.

Provides aliases prefixed with “Immutable” for all core classes. These are useful for avoiding namespace collisions when importing from the package, particularly with common names like “Dictionary” or “Namespace”.

For example, instead of:

>>> from frigid import Dictionary
>>> # Possible conflict with other Dictionary classes

you could use:

>>> from frigid.qaliases import ImmutableDictionary
>>> # Clearly indicates the source and behavior
frigid.qaliases.AbstractImmutableDictionary

alias of AbstractDictionary

frigid.qaliases.ImmutableABCFactory

alias of ABCFactory

frigid.qaliases.ImmutableClass

alias of Class

frigid.qaliases.ImmutableDictionary

alias of Dictionary

frigid.qaliases.ImmutableModule

alias of Module

frigid.qaliases.ImmutableNamespace

alias of Namespace

frigid.qaliases.ImmutableObject

alias of Object

frigid.qaliases.ImmutableProtocolClass

alias of ProtocolClass

frigid.qaliases.ImmutableValidatorDictionary

alias of ValidatorDictionary

frigid.qaliases.immutable(class_: type[C]) type[C]

Decorator which makes class immutable after initialization.

Cannot be applied to classes which define their own __setattr__ or __delattr__ methods.

frigid.qaliases.reclassify_modules_as_immutable(attributes: Annotated[Mapping[str, Any] | ModuleType | str, Doc('Module, module name, or dictionary of object attributes.')], recursive: Annotated[bool, Doc('Recursively reclassify package modules?')] = False) None

Reclassifies modules to be immutable.

This function converts existing modules to immutable modules, enforcing attribute immutability after conversion. It can operate on individual modules or entire package hierarchies.

Notes

  • Only converts modules within the same package to prevent unintended modifications to external modules

  • When used with a dictionary, converts any module objects found as values if they belong to the same package

  • Module conversion is permanent for the runtime session

  • Has no effect on already-immutable modules