_frozenlist.pyx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. # cython: freethreading_compatible = True
  2. # distutils: language = c++
  3. from cpython.bool cimport PyBool_FromLong
  4. from libcpp.atomic cimport atomic
  5. import copy
  6. import types
  7. from collections.abc import MutableSequence
  8. cdef class FrozenList:
  9. __class_getitem__ = classmethod(types.GenericAlias)
  10. cdef atomic[bint] _frozen
  11. cdef list _items
  12. def __init__(self, items=None):
  13. self._frozen.store(False)
  14. if items is not None:
  15. items = list(items)
  16. else:
  17. items = []
  18. self._items = items
  19. @property
  20. def frozen(self):
  21. return PyBool_FromLong(self._frozen.load())
  22. cdef object _check_frozen(self):
  23. if self._frozen.load():
  24. raise RuntimeError("Cannot modify frozen list.")
  25. cdef inline object _fast_len(self):
  26. return len(self._items)
  27. def freeze(self):
  28. self._frozen.store(True)
  29. def __getitem__(self, index):
  30. return self._items[index]
  31. def __setitem__(self, index, value):
  32. self._check_frozen()
  33. self._items[index] = value
  34. def __delitem__(self, index):
  35. self._check_frozen()
  36. del self._items[index]
  37. def __len__(self):
  38. return self._fast_len()
  39. def __iter__(self):
  40. return self._items.__iter__()
  41. def __reversed__(self):
  42. return self._items.__reversed__()
  43. def __richcmp__(self, other, op):
  44. if op == 0: # <
  45. return list(self) < other
  46. if op == 1: # <=
  47. return list(self) <= other
  48. if op == 2: # ==
  49. return list(self) == other
  50. if op == 3: # !=
  51. return list(self) != other
  52. if op == 4: # >
  53. return list(self) > other
  54. if op == 5: # =>
  55. return list(self) >= other
  56. def insert(self, pos, item):
  57. self._check_frozen()
  58. self._items.insert(pos, item)
  59. def __contains__(self, item):
  60. return item in self._items
  61. def __iadd__(self, items):
  62. self._check_frozen()
  63. self._items += list(items)
  64. return self
  65. def index(self, item):
  66. return self._items.index(item)
  67. def remove(self, item):
  68. self._check_frozen()
  69. self._items.remove(item)
  70. def clear(self):
  71. self._check_frozen()
  72. self._items.clear()
  73. def extend(self, items):
  74. self._check_frozen()
  75. self._items += list(items)
  76. def reverse(self):
  77. self._check_frozen()
  78. self._items.reverse()
  79. def pop(self, index=-1):
  80. self._check_frozen()
  81. return self._items.pop(index)
  82. def append(self, item):
  83. self._check_frozen()
  84. return self._items.append(item)
  85. def count(self, item):
  86. return self._items.count(item)
  87. def __repr__(self):
  88. return '<FrozenList(frozen={}, {!r})>'.format(self._frozen.load(),
  89. self._items)
  90. def __hash__(self):
  91. if self._frozen.load():
  92. return hash(tuple(self._items))
  93. else:
  94. raise RuntimeError("Cannot hash unfrozen list.")
  95. def __deepcopy__(self, memo):
  96. cdef FrozenList new_list
  97. obj_id = id(self)
  98. # Return existing copy if already processed (circular reference)
  99. if obj_id in memo:
  100. return memo[obj_id]
  101. # Create new instance and register immediately
  102. new_list = self.__class__([])
  103. memo[obj_id] = new_list
  104. # Deep copy items
  105. new_list._items[:] = [copy.deepcopy(item, memo) for item in self._items]
  106. # Preserve frozen state
  107. if self._frozen.load():
  108. new_list.freeze()
  109. return new_list
  110. MutableSequence.register(FrozenList)