docs for muutils v0.8.0
View Source on GitHub

muutils.misc.freezing


  1from __future__ import annotations
  2
  3
  4class FrozenDict(dict):
  5    def __setitem__(self, key, value):
  6        raise AttributeError("dict is frozen")
  7
  8    def __delitem__(self, key):
  9        raise AttributeError("dict is frozen")
 10
 11
 12class FrozenList(list):
 13    def __setitem__(self, index, value):
 14        raise AttributeError("list is frozen")
 15
 16    def __delitem__(self, index):
 17        raise AttributeError("list is frozen")
 18
 19    def append(self, value):
 20        raise AttributeError("list is frozen")
 21
 22    def extend(self, iterable):
 23        raise AttributeError("list is frozen")
 24
 25    def insert(self, index, value):
 26        raise AttributeError("list is frozen")
 27
 28    def remove(self, value):
 29        raise AttributeError("list is frozen")
 30
 31    def pop(self, index=-1):
 32        raise AttributeError("list is frozen")
 33
 34    def clear(self):
 35        raise AttributeError("list is frozen")
 36
 37
 38def freeze(instance: object) -> object:
 39    """recursively freeze an object in-place so that its attributes and elements cannot be changed
 40
 41    messy in the sense that sometimes the object is modified in place, but you can't rely on that. always use the return value.
 42
 43    the [gelidum](https://github.com/diegojromerolopez/gelidum/) package is a more complete implementation of this idea
 44
 45    """
 46
 47    # mark as frozen
 48    if hasattr(instance, "_IS_FROZEN"):
 49        if instance._IS_FROZEN:
 50            return instance
 51
 52    # try to mark as frozen
 53    try:
 54        instance._IS_FROZEN = True  # type: ignore[attr-defined]
 55    except AttributeError:
 56        pass
 57
 58    # skip basic types, weird things, or already frozen things
 59    if isinstance(instance, (bool, int, float, str, bytes)):
 60        pass
 61
 62    elif isinstance(instance, (type(None), type(Ellipsis))):
 63        pass
 64
 65    elif isinstance(instance, (FrozenList, FrozenDict, frozenset)):
 66        pass
 67
 68    # handle containers
 69    elif isinstance(instance, list):
 70        for i in range(len(instance)):
 71            instance[i] = freeze(instance[i])
 72        instance = FrozenList(instance)
 73
 74    elif isinstance(instance, tuple):
 75        instance = tuple(freeze(item) for item in instance)
 76
 77    elif isinstance(instance, set):
 78        instance = frozenset({freeze(item) for item in instance})
 79
 80    elif isinstance(instance, dict):
 81        for key, value in instance.items():
 82            instance[key] = freeze(value)
 83        instance = FrozenDict(instance)
 84
 85    # handle custom classes
 86    else:
 87        # set everything in the __dict__ to frozen
 88        instance.__dict__ = freeze(instance.__dict__)  # type: ignore[assignment]
 89
 90        # create a new class which inherits from the original class
 91        class FrozenClass(instance.__class__):  # type: ignore[name-defined]
 92            def __setattr__(self, name, value):
 93                raise AttributeError("class is frozen")
 94
 95        FrozenClass.__name__ = f"FrozenClass__{instance.__class__.__name__}"
 96        FrozenClass.__module__ = instance.__class__.__module__
 97        FrozenClass.__doc__ = instance.__class__.__doc__
 98
 99        # set the instance's class to the new class
100        try:
101            instance.__class__ = FrozenClass
102        except TypeError as e:
103            raise TypeError(
104                f"Cannot freeze:\n{instance = }\n{instance.__class__ = }\n{FrozenClass = }"
105            ) from e
106
107    return instance

class FrozenDict(builtins.dict):
 5class FrozenDict(dict):
 6    def __setitem__(self, key, value):
 7        raise AttributeError("dict is frozen")
 8
 9    def __delitem__(self, key):
10        raise AttributeError("dict is frozen")
Inherited Members
builtins.dict
get
setdefault
pop
popitem
keys
items
values
update
fromkeys
clear
copy
class FrozenList(builtins.list):
13class FrozenList(list):
14    def __setitem__(self, index, value):
15        raise AttributeError("list is frozen")
16
17    def __delitem__(self, index):
18        raise AttributeError("list is frozen")
19
20    def append(self, value):
21        raise AttributeError("list is frozen")
22
23    def extend(self, iterable):
24        raise AttributeError("list is frozen")
25
26    def insert(self, index, value):
27        raise AttributeError("list is frozen")
28
29    def remove(self, value):
30        raise AttributeError("list is frozen")
31
32    def pop(self, index=-1):
33        raise AttributeError("list is frozen")
34
35    def clear(self):
36        raise AttributeError("list is frozen")

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

def append(self, value):
20    def append(self, value):
21        raise AttributeError("list is frozen")

Append object to the end of the list.

def extend(self, iterable):
23    def extend(self, iterable):
24        raise AttributeError("list is frozen")

Extend list by appending elements from the iterable.

def insert(self, index, value):
26    def insert(self, index, value):
27        raise AttributeError("list is frozen")

Insert object before index.

def remove(self, value):
29    def remove(self, value):
30        raise AttributeError("list is frozen")

Remove first occurrence of value.

Raises ValueError if the value is not present.

def pop(self, index=-1):
32    def pop(self, index=-1):
33        raise AttributeError("list is frozen")

Remove and return item at index (default last).

Raises IndexError if list is empty or index is out of range.

def clear(self):
35    def clear(self):
36        raise AttributeError("list is frozen")

Remove all items from list.

Inherited Members
builtins.list
list
copy
index
count
reverse
sort
def freeze(instance: object) -> object:
 39def freeze(instance: object) -> object:
 40    """recursively freeze an object in-place so that its attributes and elements cannot be changed
 41
 42    messy in the sense that sometimes the object is modified in place, but you can't rely on that. always use the return value.
 43
 44    the [gelidum](https://github.com/diegojromerolopez/gelidum/) package is a more complete implementation of this idea
 45
 46    """
 47
 48    # mark as frozen
 49    if hasattr(instance, "_IS_FROZEN"):
 50        if instance._IS_FROZEN:
 51            return instance
 52
 53    # try to mark as frozen
 54    try:
 55        instance._IS_FROZEN = True  # type: ignore[attr-defined]
 56    except AttributeError:
 57        pass
 58
 59    # skip basic types, weird things, or already frozen things
 60    if isinstance(instance, (bool, int, float, str, bytes)):
 61        pass
 62
 63    elif isinstance(instance, (type(None), type(Ellipsis))):
 64        pass
 65
 66    elif isinstance(instance, (FrozenList, FrozenDict, frozenset)):
 67        pass
 68
 69    # handle containers
 70    elif isinstance(instance, list):
 71        for i in range(len(instance)):
 72            instance[i] = freeze(instance[i])
 73        instance = FrozenList(instance)
 74
 75    elif isinstance(instance, tuple):
 76        instance = tuple(freeze(item) for item in instance)
 77
 78    elif isinstance(instance, set):
 79        instance = frozenset({freeze(item) for item in instance})
 80
 81    elif isinstance(instance, dict):
 82        for key, value in instance.items():
 83            instance[key] = freeze(value)
 84        instance = FrozenDict(instance)
 85
 86    # handle custom classes
 87    else:
 88        # set everything in the __dict__ to frozen
 89        instance.__dict__ = freeze(instance.__dict__)  # type: ignore[assignment]
 90
 91        # create a new class which inherits from the original class
 92        class FrozenClass(instance.__class__):  # type: ignore[name-defined]
 93            def __setattr__(self, name, value):
 94                raise AttributeError("class is frozen")
 95
 96        FrozenClass.__name__ = f"FrozenClass__{instance.__class__.__name__}"
 97        FrozenClass.__module__ = instance.__class__.__module__
 98        FrozenClass.__doc__ = instance.__class__.__doc__
 99
100        # set the instance's class to the new class
101        try:
102            instance.__class__ = FrozenClass
103        except TypeError as e:
104            raise TypeError(
105                f"Cannot freeze:\n{instance = }\n{instance.__class__ = }\n{FrozenClass = }"
106            ) from e
107
108    return instance

recursively freeze an object in-place so that its attributes and elements cannot be changed

messy in the sense that sometimes the object is modified in place, but you can't rely on that. always use the return value.

the gelidum package is a more complete implementation of this idea