codekingpro/portable-devtools
114k
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
7from cpython.dict cimport PyDict_New, PyDict_Update, PyDict_Size
8
9
10def _readonly_fn(obj):
11 raise TypeError(
12 "%s object is immutable and/or readonly" % obj.__class__.__name__)
13
14
15def _immutable_fn(obj):
16 raise TypeError(
17 "%s object is immutable" % obj.__class__.__name__)
18
19
20class ReadOnlyContainer:
21
22 __slots__ = ()
23
24 def _readonly(self, *a,**kw):
25 _readonly_fn(self)
26
27 __delitem__ = __setitem__ = __setattr__ = _readonly
28
29
30class ImmutableDictBase(dict):
31 def _immutable(self, *a,**kw):
32 _immutable_fn(self)
33
34 @classmethod
35 def __class_getitem__(cls, key):
36 return cls
37
38 __delitem__ = __setitem__ = __setattr__ = _immutable
39 clear = pop = popitem = setdefault = update = _immutable
40
41
42cdef class immutabledict(dict):
43 def __repr__(self):
44 return f"immutabledict({dict.__repr__(self)})"
45
46 @classmethod
47 def __class_getitem__(cls, key):
48 return cls
49
50 def union(self, *args, **kw):
51 cdef dict to_merge = None
52 cdef immutabledict result
53 cdef Py_ssize_t args_len = len(args)
54 if args_len > 1:
55 raise TypeError(
56 f'union expected at most 1 argument, got {args_len}'
57 )
58 if args_len == 1:
59 attribute = args[0]
60 if isinstance(attribute, dict):
61 to_merge = <dict> attribute
62 if to_merge is None:
63 to_merge = dict(*args, **kw)
64
65 if PyDict_Size(to_merge) == 0:
66 return self
67
68 # new + update is faster than immutabledict(self)
69 result = immutabledict()
70 PyDict_Update(result, self)
71 PyDict_Update(result, to_merge)
72 return result
73
74 def merge_with(self, *other):
75 cdef immutabledict result = None
76 cdef object d
77 cdef bint update = False
78 if not other:
79 return self
80 for d in other:
81 if d:
82 if update == False:
83 update = True
84 # new + update is faster than immutabledict(self)
85 result = immutabledict()
86 PyDict_Update(result, self)
87 PyDict_Update(
88 result, <dict>(d if isinstance(d, dict) else dict(d))
89 )
90
91 return self if update == False else result
92
93 def copy(self):
94 return self
95
96 def __reduce__(self):
97 return immutabledict, (dict(self), )
98
99 def __delitem__(self, k):
100 _immutable_fn(self)
101
102 def __setitem__(self, k, v):
103 _immutable_fn(self)
104
105 def __setattr__(self, k, v):
106 _immutable_fn(self)
107
108 def clear(self, *args, **kw):
109 _immutable_fn(self)
110
111 def pop(self, *args, **kw):
112 _immutable_fn(self)
113
114 def popitem(self, *args, **kw):
115 _immutable_fn(self)
116
117 def setdefault(self, *args, **kw):
118 _immutable_fn(self)
119
120 def update(self, *args, **kw):
121 _immutable_fn(self)
122
123 # PEP 584
124 def __ior__(self, other):
125 _immutable_fn(self)
126
127 def __or__(self, other):
128 return immutabledict(dict.__or__(self, other))
129
130 def __ror__(self, other):
131 # NOTE: this is used only in cython 3.x;
132 # version 0.x will call __or__ with args inversed
133 return immutabledict(dict.__ror__(self, other))
134 