immutabledict.pyx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # cyextension/immutabledict.pyx
  2. # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. from cpython.dict cimport PyDict_New, PyDict_Update, PyDict_Size
  8. def _readonly_fn(obj):
  9. raise TypeError(
  10. "%s object is immutable and/or readonly" % obj.__class__.__name__)
  11. def _immutable_fn(obj):
  12. raise TypeError(
  13. "%s object is immutable" % obj.__class__.__name__)
  14. class ReadOnlyContainer:
  15. __slots__ = ()
  16. def _readonly(self, *a,**kw):
  17. _readonly_fn(self)
  18. __delitem__ = __setitem__ = __setattr__ = _readonly
  19. class ImmutableDictBase(dict):
  20. def _immutable(self, *a,**kw):
  21. _immutable_fn(self)
  22. @classmethod
  23. def __class_getitem__(cls, key):
  24. return cls
  25. __delitem__ = __setitem__ = __setattr__ = _immutable
  26. clear = pop = popitem = setdefault = update = _immutable
  27. cdef class immutabledict(dict):
  28. def __repr__(self):
  29. return f"immutabledict({dict.__repr__(self)})"
  30. @classmethod
  31. def __class_getitem__(cls, key):
  32. return cls
  33. def union(self, *args, **kw):
  34. cdef dict to_merge = None
  35. cdef immutabledict result
  36. cdef Py_ssize_t args_len = len(args)
  37. if args_len > 1:
  38. raise TypeError(
  39. f'union expected at most 1 argument, got {args_len}'
  40. )
  41. if args_len == 1:
  42. attribute = args[0]
  43. if isinstance(attribute, dict):
  44. to_merge = <dict> attribute
  45. if to_merge is None:
  46. to_merge = dict(*args, **kw)
  47. if PyDict_Size(to_merge) == 0:
  48. return self
  49. # new + update is faster than immutabledict(self)
  50. result = immutabledict()
  51. PyDict_Update(result, self)
  52. PyDict_Update(result, to_merge)
  53. return result
  54. def merge_with(self, *other):
  55. cdef immutabledict result = None
  56. cdef object d
  57. cdef bint update = False
  58. if not other:
  59. return self
  60. for d in other:
  61. if d:
  62. if update == False:
  63. update = True
  64. # new + update is faster than immutabledict(self)
  65. result = immutabledict()
  66. PyDict_Update(result, self)
  67. PyDict_Update(
  68. result, <dict>(d if isinstance(d, dict) else dict(d))
  69. )
  70. return self if update == False else result
  71. def copy(self):
  72. return self
  73. def __reduce__(self):
  74. return immutabledict, (dict(self), )
  75. def __delitem__(self, k):
  76. _immutable_fn(self)
  77. def __setitem__(self, k, v):
  78. _immutable_fn(self)
  79. def __setattr__(self, k, v):
  80. _immutable_fn(self)
  81. def clear(self, *args, **kw):
  82. _immutable_fn(self)
  83. def pop(self, *args, **kw):
  84. _immutable_fn(self)
  85. def popitem(self, *args, **kw):
  86. _immutable_fn(self)
  87. def setdefault(self, *args, **kw):
  88. _immutable_fn(self)
  89. def update(self, *args, **kw):
  90. _immutable_fn(self)
  91. # PEP 584
  92. def __ior__(self, other):
  93. _immutable_fn(self)
  94. def __or__(self, other):
  95. return immutabledict(dict.__or__(self, other))
  96. def __ror__(self, other):
  97. # NOTE: this is used only in cython 3.x;
  98. # version 0.x will call __or__ with args inversed
  99. return immutabledict(dict.__ror__(self, other))