docs for muutils v0.8.5
View Source on GitHub

muutils.misc.freezing


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

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

Append object to the end of the list.

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

Extend list by appending elements from the iterable.

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

Insert object before index.

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

Remove first occurrence of value.

Raises ValueError if the value is not present.

def pop(self, index=-1):
33    def pop(self, index=-1):
34        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):
36    def clear(self):
37        raise AttributeError("list is frozen")

Remove all items from list.

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