datahold 4.0.0.dev26__tar.gz → 4.0.0.dev27__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/PKG-INFO +1 -1
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/pyproject.toml +1 -1
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold/__init__.py +18 -5
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/SOURCES.txt +1 -0
- datahold-4.0.0.dev27/tests/test_MutableDictSlot.py +403 -0
- datahold-4.0.0.dev27/tests/test_MutableSetSlot.py +477 -0
- datahold-4.0.0.dev26/tests/test_MutableDictSlot.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/README.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/run_tests.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/setup.cfg +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/top_level.txt +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_FrozenListSlot.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_MutableListSlot.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_abstractmethods.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_all.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_attributes.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_docs.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_generic.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_internal_comparison.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_issubclass.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_name.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_nested.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_sorted.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_variance.py +0 -0
- {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/testdata.toml +0 -0
|
@@ -260,12 +260,12 @@ class SetLike[Item: abc.Hashable](Set[Item]):
|
|
|
260
260
|
return type(self)(self.__frozen__().intersection(*others))
|
|
261
261
|
|
|
262
262
|
@setdoc.basic
|
|
263
|
-
def issubset(self: Self, other: abc.Iterable[abc.Hashable], /) ->
|
|
264
|
-
return
|
|
263
|
+
def issubset(self: Self, other: abc.Iterable[abc.Hashable], /) -> bool:
|
|
264
|
+
return self.__frozen__().issubset(other)
|
|
265
265
|
|
|
266
266
|
@setdoc.basic
|
|
267
|
-
def issuperset(self: Self, other: abc.Iterable[abc.Hashable], /) ->
|
|
268
|
-
return
|
|
267
|
+
def issuperset(self: Self, other: abc.Iterable[abc.Hashable], /) -> bool:
|
|
268
|
+
return self.__frozen__().issuperset(other)
|
|
269
269
|
|
|
270
270
|
@setdoc.basic
|
|
271
271
|
def symmetric_difference(self: Self, other: abc.Iterable[Item], /) -> Self:
|
|
@@ -273,7 +273,7 @@ class SetLike[Item: abc.Hashable](Set[Item]):
|
|
|
273
273
|
|
|
274
274
|
@setdoc.basic
|
|
275
275
|
def union(self: Self, /, *others: abc.Iterable[Item]) -> Self:
|
|
276
|
-
return type(self)(self.__frozen__().union(others))
|
|
276
|
+
return type(self)(self.__frozen__().union(*others))
|
|
277
277
|
|
|
278
278
|
|
|
279
279
|
class FrozenSetLike[Item: abc.Hashable](SetLike[Item], abc.Hashable):
|
|
@@ -305,6 +305,11 @@ class MutableSetLike[Item: abc.Hashable](SetLike[Item], MutableSet[Item]):
|
|
|
305
305
|
@abstractmethod
|
|
306
306
|
@setdoc.basic
|
|
307
307
|
def __mutate__(self: Self) -> ContextManager[__Mutable__[Item]]: ...
|
|
308
|
+
|
|
309
|
+
@setdoc.basic
|
|
310
|
+
def copy(self: Self, /) -> Self:
|
|
311
|
+
return type(self)(self)
|
|
312
|
+
|
|
308
313
|
@setdoc.basic
|
|
309
314
|
def difference_update(
|
|
310
315
|
self: Self, /, *others: abc.Iterable[abc.Hashable]
|
|
@@ -525,6 +530,14 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
525
530
|
def copy(self: Self, /) -> Self:
|
|
526
531
|
return type(self)(self)
|
|
527
532
|
|
|
533
|
+
@setdoc.basic
|
|
534
|
+
def popitem(self: Self, /) -> tuple[Key | str, Optional[Value]]:
|
|
535
|
+
# for most dict d
|
|
536
|
+
# dict.popitem(d) and collections.abc.MutableMapping.popitem(d)
|
|
537
|
+
# behave differently
|
|
538
|
+
with self.__mutate__() as mutable:
|
|
539
|
+
return mutable.popitem()
|
|
540
|
+
|
|
528
541
|
|
|
529
542
|
### DICT-SLOT ###
|
|
530
543
|
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from collections import abc
|
|
3
|
+
from typing import Any, Self
|
|
4
|
+
|
|
5
|
+
from datahold import MutableDictSlot
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestClear(unittest.TestCase):
|
|
9
|
+
def test_clear_removes_all_entries(self: Self) -> None:
|
|
10
|
+
value: MutableDictSlot[Any, Any]
|
|
11
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
12
|
+
value.clear()
|
|
13
|
+
self.assertEqual(value, {})
|
|
14
|
+
self.assertEqual(len(value), 0)
|
|
15
|
+
|
|
16
|
+
def test_clear_empty_mapping(self: Self) -> None:
|
|
17
|
+
value: MutableDictSlot[Any, Any]
|
|
18
|
+
value = MutableDictSlot()
|
|
19
|
+
value.clear()
|
|
20
|
+
self.assertEqual(value, {})
|
|
21
|
+
self.assertFalse(value)
|
|
22
|
+
|
|
23
|
+
def test_clear_returns_none(self: Self) -> None:
|
|
24
|
+
result: Any
|
|
25
|
+
value: MutableDictSlot[Any, Any]
|
|
26
|
+
value = MutableDictSlot({"a": 1})
|
|
27
|
+
result = value.clear() # type: ignore[func-returns-value]
|
|
28
|
+
self.assertIsNone(result)
|
|
29
|
+
self.assertEqual(value, {})
|
|
30
|
+
|
|
31
|
+
def test_clear_updates_existing_views(self: Self) -> None:
|
|
32
|
+
items_view: abc.Collection[Any]
|
|
33
|
+
keys_view: abc.Collection[Any]
|
|
34
|
+
value: MutableDictSlot[Any, Any]
|
|
35
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
36
|
+
keys_view = value.keys()
|
|
37
|
+
items_view = value.items()
|
|
38
|
+
value.clear()
|
|
39
|
+
self.assertEqual(list(keys_view), [])
|
|
40
|
+
self.assertEqual(list(items_view), [])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TestCopy(unittest.TestCase):
|
|
44
|
+
def test_copy_has_equal_contents(self: Self) -> None:
|
|
45
|
+
copied: MutableDictSlot[Any, Any]
|
|
46
|
+
original: MutableDictSlot[Any, Any]
|
|
47
|
+
original = MutableDictSlot({"a": 1, "b": 2})
|
|
48
|
+
copied = original.copy()
|
|
49
|
+
self.assertEqual(copied, original)
|
|
50
|
+
self.assertIsNot(copied, original)
|
|
51
|
+
|
|
52
|
+
def test_copy_of_empty_mapping(self: Self) -> None:
|
|
53
|
+
copied: MutableDictSlot[Any, Any]
|
|
54
|
+
original: MutableDictSlot[Any, Any]
|
|
55
|
+
original = MutableDictSlot()
|
|
56
|
+
copied = original.copy()
|
|
57
|
+
self.assertEqual(copied, {})
|
|
58
|
+
self.assertIsNot(copied, original)
|
|
59
|
+
|
|
60
|
+
def test_copy_is_shallow(self: Self) -> None:
|
|
61
|
+
copied: MutableDictSlot[Any, Any]
|
|
62
|
+
nested: list[int]
|
|
63
|
+
original: MutableDictSlot[Any, Any]
|
|
64
|
+
nested = [1, 2]
|
|
65
|
+
original = MutableDictSlot({"nested": nested})
|
|
66
|
+
copied = original.copy()
|
|
67
|
+
copied["nested"].append(3) # type: ignore
|
|
68
|
+
self.assertIs(copied["nested"], original["nested"])
|
|
69
|
+
self.assertEqual(original["nested"], [1, 2, 3])
|
|
70
|
+
|
|
71
|
+
def test_copy_has_independent_top_level_entries(self: Self) -> None:
|
|
72
|
+
copied: MutableDictSlot[Any, Any]
|
|
73
|
+
original: MutableDictSlot[Any, Any]
|
|
74
|
+
original = MutableDictSlot({"a": 1})
|
|
75
|
+
copied = original.copy()
|
|
76
|
+
copied["a"] = 2
|
|
77
|
+
copied["b"] = 3
|
|
78
|
+
self.assertEqual(original, {"a": 1})
|
|
79
|
+
self.assertEqual(copied, {"a": 2, "b": 3})
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestFromKeys(unittest.TestCase):
|
|
83
|
+
def test_fromkeys_uses_none_by_default(self: Self) -> None:
|
|
84
|
+
result: MutableDictSlot[Any, Any]
|
|
85
|
+
result = MutableDictSlot.fromkeys(["a", "b", "c"])
|
|
86
|
+
self.assertEqual(result, {"a": None, "b": None, "c": None})
|
|
87
|
+
self.assertEqual(list(result), ["a", "b", "c"])
|
|
88
|
+
|
|
89
|
+
def test_fromkeys_uses_supplied_value(self: Self) -> None:
|
|
90
|
+
result: MutableDictSlot[Any, Any]
|
|
91
|
+
result = MutableDictSlot.fromkeys(["a", "b"], 10)
|
|
92
|
+
self.assertEqual(result, {"a": 10, "b": 10})
|
|
93
|
+
self.assertEqual(result["a"], 10)
|
|
94
|
+
|
|
95
|
+
def test_fromkeys_ignores_duplicate_keys(self: Self) -> None:
|
|
96
|
+
result: MutableDictSlot[Any, Any]
|
|
97
|
+
result = MutableDictSlot.fromkeys(["a", "b", "a", "c", "b"], 0)
|
|
98
|
+
self.assertEqual(result, {"a": 0, "b": 0, "c": 0})
|
|
99
|
+
self.assertEqual(list(result), ["a", "b", "c"])
|
|
100
|
+
|
|
101
|
+
def test_fromkeys_reuses_same_mutable_value(self: Self) -> None:
|
|
102
|
+
result: MutableDictSlot[Any, Any]
|
|
103
|
+
shared: list[Any]
|
|
104
|
+
shared = []
|
|
105
|
+
result = MutableDictSlot.fromkeys(["a", "b"], shared)
|
|
106
|
+
result["a"].append(1) # type: ignore
|
|
107
|
+
self.assertIs(result["a"], shared)
|
|
108
|
+
self.assertIs(result["b"], shared)
|
|
109
|
+
self.assertEqual(result["b"], [1])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class TestGet(unittest.TestCase):
|
|
113
|
+
def test_get_returns_existing_value(self: Self) -> None:
|
|
114
|
+
value: MutableDictSlot[Any, Any]
|
|
115
|
+
value = MutableDictSlot({"a": 1})
|
|
116
|
+
self.assertEqual(value.get("a"), 1)
|
|
117
|
+
self.assertEqual(value, {"a": 1})
|
|
118
|
+
|
|
119
|
+
def test_get_returns_none_for_missing_key(self: Self) -> None:
|
|
120
|
+
result: Any
|
|
121
|
+
value: MutableDictSlot[Any, Any]
|
|
122
|
+
value = MutableDictSlot({"a": 1})
|
|
123
|
+
result = value.get("missing")
|
|
124
|
+
self.assertIsNone(result)
|
|
125
|
+
self.assertNotIn("missing", value)
|
|
126
|
+
|
|
127
|
+
def test_get_returns_custom_default(self: Self) -> None:
|
|
128
|
+
default: object
|
|
129
|
+
value: MutableDictSlot[Any, Any]
|
|
130
|
+
value = MutableDictSlot()
|
|
131
|
+
default = object()
|
|
132
|
+
result = value.get("missing", default)
|
|
133
|
+
self.assertIs(result, default)
|
|
134
|
+
self.assertEqual(value, {})
|
|
135
|
+
|
|
136
|
+
def test_get_rejects_unhashable_key(self: Self) -> None:
|
|
137
|
+
value: MutableDictSlot[Any, Any]
|
|
138
|
+
value = MutableDictSlot({"a": 1})
|
|
139
|
+
with self.assertRaises(TypeError):
|
|
140
|
+
value.get([])
|
|
141
|
+
self.assertEqual(value, {"a": 1})
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestItems(unittest.TestCase):
|
|
145
|
+
def test_items_contains_key_value_pairs(self: Self) -> None:
|
|
146
|
+
result: abc.Collection[Any]
|
|
147
|
+
value: MutableDictSlot[Any, Any]
|
|
148
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
149
|
+
result = value.items()
|
|
150
|
+
self.assertEqual(set(result), {("a", 1), ("b", 2)})
|
|
151
|
+
self.assertEqual(len(result), 2)
|
|
152
|
+
|
|
153
|
+
def test_items_preserves_iteration_order(self: Self) -> None:
|
|
154
|
+
value: MutableDictSlot[Any, Any]
|
|
155
|
+
value = MutableDictSlot()
|
|
156
|
+
value["first"] = 1
|
|
157
|
+
value["second"] = 2
|
|
158
|
+
value["third"] = 3
|
|
159
|
+
self.assertEqual(
|
|
160
|
+
list(value.items()),
|
|
161
|
+
[("first", 1), ("second", 2), ("third", 3)],
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def test_items_view_is_dynamic(self: Self) -> None:
|
|
165
|
+
result: abc.Collection[Any]
|
|
166
|
+
value: MutableDictSlot[Any, Any]
|
|
167
|
+
value = MutableDictSlot({"a": 1})
|
|
168
|
+
result = value.items()
|
|
169
|
+
value["a"] = 2
|
|
170
|
+
value["b"] = 3
|
|
171
|
+
self.assertEqual(list(result), [("a", 2), ("b", 3)])
|
|
172
|
+
|
|
173
|
+
def test_items_supports_pair_membership(self: Self) -> None:
|
|
174
|
+
result: abc.Collection[Any]
|
|
175
|
+
value: MutableDictSlot[Any, Any]
|
|
176
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
177
|
+
result = value.items()
|
|
178
|
+
self.assertIn(("a", 1), result)
|
|
179
|
+
self.assertIn(("b", 2), result)
|
|
180
|
+
self.assertNotIn(("a", 2), result)
|
|
181
|
+
self.assertNotIn(("missing", 1), result)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class TestKeys(unittest.TestCase):
|
|
185
|
+
def test_keys_contains_all_keys(self: Self) -> None:
|
|
186
|
+
result: abc.Collection[Any]
|
|
187
|
+
value: MutableDictSlot[Any, Any]
|
|
188
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
189
|
+
result = value.keys()
|
|
190
|
+
self.assertEqual(set(result), {"a", "b"})
|
|
191
|
+
self.assertEqual(len(result), 2)
|
|
192
|
+
|
|
193
|
+
def test_keys_preserves_iteration_order(self: Self) -> None:
|
|
194
|
+
value: MutableDictSlot[Any, Any]
|
|
195
|
+
value = MutableDictSlot()
|
|
196
|
+
value["first"] = 1
|
|
197
|
+
value["second"] = 2
|
|
198
|
+
value["third"] = 3
|
|
199
|
+
self.assertEqual(
|
|
200
|
+
list(value.keys()),
|
|
201
|
+
["first", "second", "third"],
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def test_keys_view_is_dynamic(self: Self) -> None:
|
|
205
|
+
result: abc.Collection[Any]
|
|
206
|
+
value: MutableDictSlot[Any, Any]
|
|
207
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
208
|
+
result = value.keys()
|
|
209
|
+
del value["a"]
|
|
210
|
+
value["c"] = 3
|
|
211
|
+
self.assertEqual(list(result), ["b", "c"])
|
|
212
|
+
|
|
213
|
+
def test_keys_supports_membership(self: Self) -> None:
|
|
214
|
+
result: abc.Collection[Any]
|
|
215
|
+
value: MutableDictSlot[Any, Any]
|
|
216
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
217
|
+
result = value.keys()
|
|
218
|
+
self.assertIn("a", result)
|
|
219
|
+
self.assertIn("b", result)
|
|
220
|
+
self.assertNotIn("missing", result)
|
|
221
|
+
self.assertNotIn(1, result)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class TestPop(unittest.TestCase):
|
|
225
|
+
def test_pop_returns_and_removes_existing_value(self: Self) -> None:
|
|
226
|
+
result: Any
|
|
227
|
+
value: MutableDictSlot[Any, Any]
|
|
228
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
229
|
+
result = value.pop("a")
|
|
230
|
+
self.assertEqual(result, 1)
|
|
231
|
+
self.assertEqual(value, {"b": 2})
|
|
232
|
+
|
|
233
|
+
def test_pop_missing_key_raises_key_error(self: Self) -> None:
|
|
234
|
+
value: MutableDictSlot[Any, Any]
|
|
235
|
+
value = MutableDictSlot({"a": 1})
|
|
236
|
+
with self.assertRaises(KeyError) as context:
|
|
237
|
+
value.pop("missing")
|
|
238
|
+
self.assertEqual(context.exception.args, ("missing",))
|
|
239
|
+
self.assertEqual(value, {"a": 1})
|
|
240
|
+
|
|
241
|
+
def test_pop_missing_key_returns_default(self: Self) -> None:
|
|
242
|
+
default: object
|
|
243
|
+
result: Any
|
|
244
|
+
value: MutableDictSlot[Any, Any]
|
|
245
|
+
value = MutableDictSlot({"a": 1})
|
|
246
|
+
default = object()
|
|
247
|
+
result = value.pop("missing", default)
|
|
248
|
+
self.assertIs(result, default)
|
|
249
|
+
self.assertEqual(value, {"a": 1})
|
|
250
|
+
|
|
251
|
+
def test_pop_handles_none_as_explicit_default(self: Self) -> None:
|
|
252
|
+
result: Any
|
|
253
|
+
value: MutableDictSlot[Any, Any]
|
|
254
|
+
value = MutableDictSlot()
|
|
255
|
+
result = value.pop("missing", None)
|
|
256
|
+
self.assertIsNone(result)
|
|
257
|
+
self.assertEqual(value, {})
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class TestPopItem(unittest.TestCase):
|
|
261
|
+
def test_popitem_removes_last_inserted_pair(self: Self) -> None:
|
|
262
|
+
result: tuple[Any, Any]
|
|
263
|
+
value: MutableDictSlot[Any, Any]
|
|
264
|
+
value = MutableDictSlot({"a": 1, "b": 2, "c": 3})
|
|
265
|
+
result = value.popitem()
|
|
266
|
+
self.assertEqual(result, ("c", 3))
|
|
267
|
+
self.assertEqual(value, {"a": 1, "b": 2})
|
|
268
|
+
|
|
269
|
+
def test_popitem_uses_lifo_order_repeatedly(self: Self) -> None:
|
|
270
|
+
first: tuple[Any, Any]
|
|
271
|
+
second: tuple[Any, Any]
|
|
272
|
+
third: tuple[Any, Any]
|
|
273
|
+
value: MutableDictSlot[Any, Any]
|
|
274
|
+
value = MutableDictSlot({"a": 1, "b": 2, "c": 3})
|
|
275
|
+
first = value.popitem()
|
|
276
|
+
second = value.popitem()
|
|
277
|
+
third = value.popitem()
|
|
278
|
+
self.assertEqual(first, ("c", 3))
|
|
279
|
+
self.assertEqual(second, ("b", 2))
|
|
280
|
+
self.assertEqual(third, ("a", 1))
|
|
281
|
+
self.assertEqual(len(value), 0)
|
|
282
|
+
|
|
283
|
+
def test_popitem_removes_only_entry(self: Self) -> None:
|
|
284
|
+
result: tuple[Any, Any]
|
|
285
|
+
value: MutableDictSlot[Any, Any]
|
|
286
|
+
value = MutableDictSlot({"a": 1})
|
|
287
|
+
result = value.popitem()
|
|
288
|
+
self.assertEqual(result, ("a", 1))
|
|
289
|
+
self.assertEqual(value, {})
|
|
290
|
+
|
|
291
|
+
def test_popitem_empty_mapping_raises_key_error(self: Self) -> None:
|
|
292
|
+
value: MutableDictSlot[Any, Any]
|
|
293
|
+
value = MutableDictSlot()
|
|
294
|
+
with self.assertRaises(KeyError):
|
|
295
|
+
value.popitem()
|
|
296
|
+
self.assertEqual(value, {})
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class TestSetDefault(unittest.TestCase):
|
|
300
|
+
def test_setdefault_returns_existing_value(self: Self) -> None:
|
|
301
|
+
result: Any
|
|
302
|
+
value: MutableDictSlot[Any, Any]
|
|
303
|
+
value = MutableDictSlot({"a": 1})
|
|
304
|
+
result = value.setdefault("a", 2)
|
|
305
|
+
self.assertEqual(result, 1)
|
|
306
|
+
self.assertEqual(value, {"a": 1})
|
|
307
|
+
|
|
308
|
+
def test_setdefault_inserts_supplied_default(self: Self) -> None:
|
|
309
|
+
result: Any
|
|
310
|
+
value: MutableDictSlot[Any, Any]
|
|
311
|
+
value = MutableDictSlot({"a": 1})
|
|
312
|
+
result = value.setdefault("b", 2)
|
|
313
|
+
self.assertEqual(result, 2)
|
|
314
|
+
self.assertEqual(value, {"a": 1, "b": 2})
|
|
315
|
+
|
|
316
|
+
def test_setdefault_inserts_none_by_default(self: Self) -> None:
|
|
317
|
+
result: Any
|
|
318
|
+
value: MutableDictSlot[Any, Any]
|
|
319
|
+
value = MutableDictSlot()
|
|
320
|
+
result = value.setdefault("a")
|
|
321
|
+
self.assertIsNone(result)
|
|
322
|
+
self.assertEqual(value, {"a": None})
|
|
323
|
+
|
|
324
|
+
def test_setdefault_preserves_mutable_default_identity(self: Self) -> None:
|
|
325
|
+
default: list[Any]
|
|
326
|
+
result: Any
|
|
327
|
+
value: MutableDictSlot[Any, Any]
|
|
328
|
+
value = MutableDictSlot()
|
|
329
|
+
default = []
|
|
330
|
+
result = value.setdefault("a", default)
|
|
331
|
+
result.append(1)
|
|
332
|
+
self.assertIs(result, default)
|
|
333
|
+
self.assertIs(value["a"], default)
|
|
334
|
+
self.assertEqual(value["a"], [1])
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class TestUpdate(unittest.TestCase):
|
|
338
|
+
def test_update_from_mapping(self: Self) -> None:
|
|
339
|
+
value: MutableDictSlot[Any, Any]
|
|
340
|
+
value = MutableDictSlot({"a": 1})
|
|
341
|
+
value.update({"b": 2, "c": 3})
|
|
342
|
+
self.assertEqual(value, {"a": 1, "b": 2, "c": 3})
|
|
343
|
+
|
|
344
|
+
def test_update_from_iterable_of_pairs(self: Self) -> None:
|
|
345
|
+
value: MutableDictSlot[Any, Any]
|
|
346
|
+
value = MutableDictSlot({"a": 1})
|
|
347
|
+
value.update([("b", 2), ("c", 3)])
|
|
348
|
+
self.assertEqual(value, {"a": 1, "b": 2, "c": 3})
|
|
349
|
+
|
|
350
|
+
def test_update_from_keyword_arguments(self: Self) -> None:
|
|
351
|
+
value: MutableDictSlot[Any, Any]
|
|
352
|
+
value = MutableDictSlot({"a": 1})
|
|
353
|
+
value.update(b=2, c=3)
|
|
354
|
+
self.assertEqual(value, {"a": 1, "b": 2, "c": 3})
|
|
355
|
+
|
|
356
|
+
def test_update_overwrites_values_and_returns_none(self: Self) -> None:
|
|
357
|
+
result: Any
|
|
358
|
+
value: MutableDictSlot[Any, Any]
|
|
359
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
360
|
+
result = value.update({"a": 10}, b=20, c=30)
|
|
361
|
+
self.assertIsNone(result)
|
|
362
|
+
self.assertEqual(value, {"a": 10, "b": 20, "c": 30})
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class TestValues(unittest.TestCase):
|
|
366
|
+
def test_values_contains_all_values(self: Self) -> None:
|
|
367
|
+
result: Any
|
|
368
|
+
value: MutableDictSlot[Any, Any]
|
|
369
|
+
value = MutableDictSlot({"a": 1, "b": 2, "c": 3})
|
|
370
|
+
result = value.values()
|
|
371
|
+
self.assertEqual(list(result), [1, 2, 3])
|
|
372
|
+
self.assertEqual(len(result), 3)
|
|
373
|
+
|
|
374
|
+
def test_values_preserves_iteration_order(self: Self) -> None:
|
|
375
|
+
value: MutableDictSlot[Any, Any]
|
|
376
|
+
value = MutableDictSlot()
|
|
377
|
+
value["first"] = 10
|
|
378
|
+
value["second"] = 20
|
|
379
|
+
value["third"] = 30
|
|
380
|
+
|
|
381
|
+
self.assertEqual(list(value.values()), [10, 20, 30])
|
|
382
|
+
|
|
383
|
+
def test_values_view_is_dynamic(self: Self) -> None:
|
|
384
|
+
result: abc.Collection[Any]
|
|
385
|
+
value: MutableDictSlot[Any, Any]
|
|
386
|
+
value = MutableDictSlot({"a": 1, "b": 2})
|
|
387
|
+
result = value.values()
|
|
388
|
+
value["a"] = 10
|
|
389
|
+
del value["b"]
|
|
390
|
+
value["c"] = 3
|
|
391
|
+
self.assertEqual(list(result), [10, 3])
|
|
392
|
+
|
|
393
|
+
def test_values_preserves_duplicate_values(self: Self) -> None:
|
|
394
|
+
result: list[Any]
|
|
395
|
+
value: MutableDictSlot[Any, Any]
|
|
396
|
+
value = MutableDictSlot({"a": 1, "b": 1, "c": 2})
|
|
397
|
+
result = list(value.values())
|
|
398
|
+
self.assertEqual(result, [1, 1, 2])
|
|
399
|
+
self.assertEqual(result.count(1), 2)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if __name__ == "__main__":
|
|
403
|
+
unittest.main()
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from typing import Any, Self
|
|
3
|
+
|
|
4
|
+
from datahold import MutableSetSlot
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestAdd(unittest.TestCase):
|
|
8
|
+
def test_adds_new_element(self: Self) -> None:
|
|
9
|
+
value: MutableSetSlot[Any]
|
|
10
|
+
value = MutableSetSlot({1, 2})
|
|
11
|
+
value.add(3)
|
|
12
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
13
|
+
|
|
14
|
+
def test_adding_duplicate_does_not_change_set(self: Self) -> None:
|
|
15
|
+
value: MutableSetSlot[Any]
|
|
16
|
+
value = MutableSetSlot({1, 2})
|
|
17
|
+
value.add(2)
|
|
18
|
+
self.assertEqual(set(value), {1, 2})
|
|
19
|
+
|
|
20
|
+
def test_adds_hashable_tuple(self: Self) -> None:
|
|
21
|
+
value: MutableSetSlot[Any]
|
|
22
|
+
value = MutableSetSlot()
|
|
23
|
+
value.add((1, 2))
|
|
24
|
+
self.assertEqual(set(value), {(1, 2)})
|
|
25
|
+
|
|
26
|
+
def test_adding_unhashable_element_raises_type_error(self: Self) -> None:
|
|
27
|
+
value: MutableSetSlot[Any]
|
|
28
|
+
value = MutableSetSlot({1})
|
|
29
|
+
with self.assertRaises(TypeError):
|
|
30
|
+
value.add([2])
|
|
31
|
+
self.assertEqual(set(value), {1})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TestClear(unittest.TestCase):
|
|
35
|
+
def test_clears_populated_set(self: Self) -> None:
|
|
36
|
+
value: MutableSetSlot[Any]
|
|
37
|
+
value = MutableSetSlot({1, 2, 3})
|
|
38
|
+
value.clear()
|
|
39
|
+
self.assertEqual(set(value), set())
|
|
40
|
+
|
|
41
|
+
def test_clearing_empty_set_keeps_it_empty(self: Self) -> None:
|
|
42
|
+
value: MutableSetSlot[Any]
|
|
43
|
+
value = MutableSetSlot()
|
|
44
|
+
value.clear()
|
|
45
|
+
self.assertEqual(set(value), set())
|
|
46
|
+
|
|
47
|
+
def test_clear_returns_none(self: Self) -> None:
|
|
48
|
+
value: MutableSetSlot[Any]
|
|
49
|
+
value = MutableSetSlot({1, 2})
|
|
50
|
+
self.assertIsNone(value.clear()) # type: ignore[func-returns-value]
|
|
51
|
+
|
|
52
|
+
def test_set_can_be_reused_after_clear(self: Self) -> None:
|
|
53
|
+
value: MutableSetSlot[Any]
|
|
54
|
+
value = MutableSetSlot({1, 2})
|
|
55
|
+
value.clear()
|
|
56
|
+
value.add(3)
|
|
57
|
+
self.assertEqual(set(value), {3})
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestCopy(unittest.TestCase):
|
|
61
|
+
def test_copy_has_same_contents(self: Self) -> None:
|
|
62
|
+
copied: MutableSetSlot[Any]
|
|
63
|
+
value: MutableSetSlot[Any]
|
|
64
|
+
value = MutableSetSlot({1, 2, 3})
|
|
65
|
+
copied = value.copy()
|
|
66
|
+
self.assertEqual(set(copied), {1, 2, 3})
|
|
67
|
+
|
|
68
|
+
def test_copy_is_a_different_object(self: Self) -> None:
|
|
69
|
+
copied: MutableSetSlot[Any]
|
|
70
|
+
value: MutableSetSlot[Any]
|
|
71
|
+
value = MutableSetSlot({1, 2})
|
|
72
|
+
copied = value.copy()
|
|
73
|
+
self.assertIsNot(copied, value)
|
|
74
|
+
|
|
75
|
+
def test_copy_is_independent_of_original(self: Self) -> None:
|
|
76
|
+
copied: MutableSetSlot[Any]
|
|
77
|
+
value: MutableSetSlot[Any]
|
|
78
|
+
value = MutableSetSlot({1, 2})
|
|
79
|
+
copied = value.copy()
|
|
80
|
+
copied.add(3)
|
|
81
|
+
self.assertEqual(set(value), {1, 2})
|
|
82
|
+
self.assertEqual(set(copied), {1, 2, 3})
|
|
83
|
+
|
|
84
|
+
def test_copy_of_empty_set_is_empty(self: Self) -> None:
|
|
85
|
+
value: MutableSetSlot[Any]
|
|
86
|
+
value = MutableSetSlot()
|
|
87
|
+
copied = value.copy()
|
|
88
|
+
self.assertEqual(set(copied), set())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class TestDifference(unittest.TestCase):
|
|
92
|
+
def test_difference_removes_shared_elements(self: Self) -> None:
|
|
93
|
+
value: MutableSetSlot[Any]
|
|
94
|
+
value = MutableSetSlot({1, 2, 3})
|
|
95
|
+
result = value.difference({2, 3, 4})
|
|
96
|
+
self.assertEqual(set(result), {1})
|
|
97
|
+
|
|
98
|
+
def test_difference_accepts_multiple_iterables(self: Self) -> None:
|
|
99
|
+
value: MutableSetSlot[Any]
|
|
100
|
+
value = MutableSetSlot({1, 2, 3, 4})
|
|
101
|
+
result = value.difference({1}, [2, 5])
|
|
102
|
+
self.assertEqual(set(result), {3, 4})
|
|
103
|
+
|
|
104
|
+
def test_difference_with_disjoint_set_is_unchanged(self: Self) -> None:
|
|
105
|
+
value: MutableSetSlot[Any]
|
|
106
|
+
value = MutableSetSlot({1, 2})
|
|
107
|
+
result = value.difference({3, 4})
|
|
108
|
+
self.assertEqual(set(result), {1, 2})
|
|
109
|
+
|
|
110
|
+
def test_difference_does_not_modify_original(self: Self) -> None:
|
|
111
|
+
value: MutableSetSlot[Any]
|
|
112
|
+
value = MutableSetSlot({1, 2, 3})
|
|
113
|
+
value.difference({2})
|
|
114
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class TestDifferenceUpdate(unittest.TestCase):
|
|
118
|
+
def test_difference_update_removes_shared_elements(self: Self) -> None:
|
|
119
|
+
value: MutableSetSlot[Any]
|
|
120
|
+
value = MutableSetSlot({1, 2, 3})
|
|
121
|
+
value.difference_update({2, 4})
|
|
122
|
+
self.assertEqual(set(value), {1, 3})
|
|
123
|
+
|
|
124
|
+
def test_difference_update_accepts_multiple_iterables(self: Self) -> None:
|
|
125
|
+
value: MutableSetSlot[Any]
|
|
126
|
+
value = MutableSetSlot({1, 2, 3, 4})
|
|
127
|
+
value.difference_update({1}, [2, 5])
|
|
128
|
+
self.assertEqual(set(value), {3, 4})
|
|
129
|
+
|
|
130
|
+
def test_difference_update_with_disjoint_set_is_unchanged(
|
|
131
|
+
self: Self,
|
|
132
|
+
) -> None:
|
|
133
|
+
value: MutableSetSlot[Any]
|
|
134
|
+
value = MutableSetSlot({1, 2})
|
|
135
|
+
value.difference_update({3, 4})
|
|
136
|
+
self.assertEqual(set(value), {1, 2})
|
|
137
|
+
|
|
138
|
+
def test_difference_update_with_self_empties_set(self: Self) -> None:
|
|
139
|
+
value: MutableSetSlot[Any]
|
|
140
|
+
value = MutableSetSlot({1, 2, 3})
|
|
141
|
+
value.difference_update(value)
|
|
142
|
+
self.assertEqual(set(value), set())
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class TestDiscard(unittest.TestCase):
|
|
146
|
+
def test_discards_existing_element(self: Self) -> None:
|
|
147
|
+
value: MutableSetSlot[Any]
|
|
148
|
+
value = MutableSetSlot({1, 2, 3})
|
|
149
|
+
value.discard(2)
|
|
150
|
+
self.assertEqual(set(value), {1, 3})
|
|
151
|
+
|
|
152
|
+
def test_discarding_missing_element_does_not_raise(self: Self) -> None:
|
|
153
|
+
value: MutableSetSlot[Any]
|
|
154
|
+
value = MutableSetSlot({1, 2})
|
|
155
|
+
value.discard(3)
|
|
156
|
+
self.assertEqual(set(value), {1, 2})
|
|
157
|
+
|
|
158
|
+
def test_discard_returns_none(self: Self) -> None:
|
|
159
|
+
value: MutableSetSlot[Any]
|
|
160
|
+
value = MutableSetSlot({1, 2})
|
|
161
|
+
self.assertIsNone(value.discard(1))
|
|
162
|
+
|
|
163
|
+
def test_discarding_unhashable_element_raises_type_error(
|
|
164
|
+
self: Self,
|
|
165
|
+
) -> None:
|
|
166
|
+
value: MutableSetSlot[Any]
|
|
167
|
+
value = MutableSetSlot({1, 2})
|
|
168
|
+
with self.assertRaises(TypeError):
|
|
169
|
+
value.discard([1]) # type: ignore
|
|
170
|
+
self.assertEqual(set(value), {1, 2})
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class TestIntersection(unittest.TestCase):
|
|
174
|
+
def test_intersection_returns_shared_elements(self: Self) -> None:
|
|
175
|
+
value: MutableSetSlot[Any]
|
|
176
|
+
value = MutableSetSlot({1, 2, 3})
|
|
177
|
+
result = value.intersection({2, 3, 4})
|
|
178
|
+
self.assertEqual(set(result), {2, 3})
|
|
179
|
+
|
|
180
|
+
def test_intersection_accepts_multiple_iterables(self: Self) -> None:
|
|
181
|
+
value: MutableSetSlot[Any]
|
|
182
|
+
value = MutableSetSlot({1, 2, 3, 4})
|
|
183
|
+
result = value.intersection({2, 3, 4}, [3, 4, 5])
|
|
184
|
+
self.assertEqual(set(result), {3, 4})
|
|
185
|
+
|
|
186
|
+
def test_intersection_with_disjoint_set_is_empty(self: Self) -> None:
|
|
187
|
+
value: MutableSetSlot[Any]
|
|
188
|
+
value = MutableSetSlot({1, 2})
|
|
189
|
+
result = value.intersection({3, 4})
|
|
190
|
+
self.assertEqual(set(result), set())
|
|
191
|
+
|
|
192
|
+
def test_intersection_does_not_modify_original(self: Self) -> None:
|
|
193
|
+
value: MutableSetSlot[Any]
|
|
194
|
+
value = MutableSetSlot({1, 2, 3})
|
|
195
|
+
value.intersection({2})
|
|
196
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class TestIntersectionUpdate(unittest.TestCase):
|
|
200
|
+
def test_intersection_update_keeps_shared_elements(self: Self) -> None:
|
|
201
|
+
value: MutableSetSlot[Any]
|
|
202
|
+
value = MutableSetSlot({1, 2, 3})
|
|
203
|
+
value.intersection_update({2, 3, 4})
|
|
204
|
+
self.assertEqual(set(value), {2, 3})
|
|
205
|
+
|
|
206
|
+
def test_intersection_update_accepts_multiple_iterables(
|
|
207
|
+
self: Self,
|
|
208
|
+
) -> None:
|
|
209
|
+
value: MutableSetSlot[Any]
|
|
210
|
+
value = MutableSetSlot({1, 2, 3, 4})
|
|
211
|
+
value.intersection_update({2, 3, 4}, [3, 4, 5])
|
|
212
|
+
self.assertEqual(set(value), {3, 4})
|
|
213
|
+
|
|
214
|
+
def test_intersection_update_with_disjoint_set_empties_set(
|
|
215
|
+
self: Self,
|
|
216
|
+
) -> None:
|
|
217
|
+
value: MutableSetSlot[Any]
|
|
218
|
+
value = MutableSetSlot({1, 2})
|
|
219
|
+
value.intersection_update({3, 4})
|
|
220
|
+
self.assertEqual(set(value), set())
|
|
221
|
+
|
|
222
|
+
def test_intersection_update_with_self_is_unchanged(self: Self) -> None:
|
|
223
|
+
value: MutableSetSlot[Any]
|
|
224
|
+
value = MutableSetSlot({1, 2, 3})
|
|
225
|
+
value.intersection_update(value)
|
|
226
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class TestIsDisjoint(unittest.TestCase):
|
|
230
|
+
def test_disjoint_sets_return_true(self: Self) -> None:
|
|
231
|
+
value: MutableSetSlot[Any]
|
|
232
|
+
value = MutableSetSlot({1, 2})
|
|
233
|
+
self.assertTrue(value.isdisjoint({3, 4}))
|
|
234
|
+
|
|
235
|
+
def test_overlapping_sets_return_false(self: Self) -> None:
|
|
236
|
+
value: MutableSetSlot[Any]
|
|
237
|
+
value = MutableSetSlot({1, 2})
|
|
238
|
+
self.assertFalse(value.isdisjoint({2, 3}))
|
|
239
|
+
|
|
240
|
+
def test_empty_set_is_disjoint_from_populated_set(self: Self) -> None:
|
|
241
|
+
value: MutableSetSlot[Any]
|
|
242
|
+
value = MutableSetSlot()
|
|
243
|
+
self.assertTrue(value.isdisjoint({1, 2}))
|
|
244
|
+
|
|
245
|
+
def test_isdisjoint_accepts_generator(self: Self) -> None:
|
|
246
|
+
value: MutableSetSlot[Any]
|
|
247
|
+
value = MutableSetSlot({1, 2})
|
|
248
|
+
other = (number for number in (3, 4))
|
|
249
|
+
self.assertTrue(value.isdisjoint(other))
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class TestIsSubset(unittest.TestCase):
|
|
253
|
+
def test_proper_subset_returns_true(self: Self) -> None:
|
|
254
|
+
value: MutableSetSlot[Any]
|
|
255
|
+
value = MutableSetSlot({1, 2})
|
|
256
|
+
self.assertTrue(value.issubset({1, 2, 3}))
|
|
257
|
+
|
|
258
|
+
def test_equal_set_is_subset(self: Self) -> None:
|
|
259
|
+
value: MutableSetSlot[Any]
|
|
260
|
+
value = MutableSetSlot({1, 2})
|
|
261
|
+
self.assertTrue(value.issubset({1, 2}))
|
|
262
|
+
|
|
263
|
+
def test_non_subset_returns_false(self: Self) -> None:
|
|
264
|
+
value: MutableSetSlot[Any]
|
|
265
|
+
value = MutableSetSlot({1, 4})
|
|
266
|
+
self.assertFalse(value.issubset({1, 2, 3}))
|
|
267
|
+
|
|
268
|
+
def test_empty_set_is_subset_of_any_set(self: Self) -> None:
|
|
269
|
+
value: MutableSetSlot[Any]
|
|
270
|
+
value = MutableSetSlot()
|
|
271
|
+
self.assertTrue(value.issubset({1, 2}))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class TestIsSuperset(unittest.TestCase):
|
|
275
|
+
def test_proper_superset_returns_true(self: Self) -> None:
|
|
276
|
+
value: MutableSetSlot[Any]
|
|
277
|
+
value = MutableSetSlot({1, 2, 3})
|
|
278
|
+
self.assertTrue(value.issuperset({1, 2}))
|
|
279
|
+
|
|
280
|
+
def test_equal_set_is_superset(self: Self) -> None:
|
|
281
|
+
value: MutableSetSlot[Any]
|
|
282
|
+
value = MutableSetSlot({1, 2})
|
|
283
|
+
self.assertTrue(value.issuperset({1, 2}))
|
|
284
|
+
|
|
285
|
+
def test_non_superset_returns_false(self: Self) -> None:
|
|
286
|
+
value: MutableSetSlot[Any]
|
|
287
|
+
value = MutableSetSlot({1, 2})
|
|
288
|
+
self.assertFalse(value.issuperset({1, 3}))
|
|
289
|
+
|
|
290
|
+
def test_every_set_is_superset_of_empty_set(self: Self) -> None:
|
|
291
|
+
value: MutableSetSlot[Any]
|
|
292
|
+
value = MutableSetSlot({1, 2})
|
|
293
|
+
self.assertTrue(value.issuperset(set()))
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class TestPop(unittest.TestCase):
|
|
297
|
+
def test_pop_returns_and_removes_only_element(self: Self) -> None:
|
|
298
|
+
value: MutableSetSlot[Any]
|
|
299
|
+
value = MutableSetSlot({1})
|
|
300
|
+
result = value.pop()
|
|
301
|
+
self.assertEqual(result, 1)
|
|
302
|
+
self.assertEqual(set(value), set())
|
|
303
|
+
|
|
304
|
+
def test_pop_from_empty_set_raises_key_error(self: Self) -> None:
|
|
305
|
+
value: MutableSetSlot[Any]
|
|
306
|
+
value = MutableSetSlot()
|
|
307
|
+
with self.assertRaises(KeyError):
|
|
308
|
+
value.pop()
|
|
309
|
+
|
|
310
|
+
def test_pop_removes_one_element_from_populated_set(self: Self) -> None:
|
|
311
|
+
original: set[int]
|
|
312
|
+
result: Any
|
|
313
|
+
value: MutableSetSlot[Any]
|
|
314
|
+
original = {1, 2, 3}
|
|
315
|
+
value = MutableSetSlot(original)
|
|
316
|
+
result = value.pop()
|
|
317
|
+
self.assertIn(result, original)
|
|
318
|
+
self.assertEqual(set(value), original - {result})
|
|
319
|
+
|
|
320
|
+
def test_repeated_pop_exhausts_set(self: Self) -> None:
|
|
321
|
+
popped: set[Any]
|
|
322
|
+
value: MutableSetSlot[Any]
|
|
323
|
+
value = MutableSetSlot({1, 2, 3})
|
|
324
|
+
popped = {value.pop(), value.pop(), value.pop()}
|
|
325
|
+
self.assertEqual(popped, {1, 2, 3})
|
|
326
|
+
self.assertEqual(set(value), set())
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class TestRemove(unittest.TestCase):
|
|
330
|
+
def test_removes_existing_element(self: Self) -> None:
|
|
331
|
+
value: MutableSetSlot[Any]
|
|
332
|
+
value = MutableSetSlot({1, 2, 3})
|
|
333
|
+
value.remove(2)
|
|
334
|
+
self.assertEqual(set(value), {1, 3})
|
|
335
|
+
|
|
336
|
+
def test_removing_missing_element_raises_key_error(self: Self) -> None:
|
|
337
|
+
value: MutableSetSlot[Any]
|
|
338
|
+
value = MutableSetSlot({1, 2})
|
|
339
|
+
with self.assertRaises(KeyError):
|
|
340
|
+
value.remove(3)
|
|
341
|
+
self.assertEqual(set(value), {1, 2})
|
|
342
|
+
|
|
343
|
+
def test_remove_returns_none(self: Self) -> None:
|
|
344
|
+
value: MutableSetSlot[Any]
|
|
345
|
+
value = MutableSetSlot({1, 2})
|
|
346
|
+
self.assertIsNone(value.remove(1)) # type: ignore[func-returns-value]
|
|
347
|
+
|
|
348
|
+
def test_removing_unhashable_element_raises_type_error(self: Self) -> None:
|
|
349
|
+
value: MutableSetSlot[Any]
|
|
350
|
+
value = MutableSetSlot({1, 2})
|
|
351
|
+
with self.assertRaises(Exception):
|
|
352
|
+
# in general you should not rely upon
|
|
353
|
+
# a TypeError or an AttributeError occuring
|
|
354
|
+
value.remove([1])
|
|
355
|
+
self.assertEqual(set(value), {1, 2})
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class TestSymmetricDifference(unittest.TestCase):
|
|
359
|
+
def test_symmetric_difference_excludes_shared_elements(self: Self) -> None:
|
|
360
|
+
result: Any
|
|
361
|
+
value: MutableSetSlot[Any]
|
|
362
|
+
value = MutableSetSlot({1, 2, 3})
|
|
363
|
+
result = value.symmetric_difference({3, 4, 5})
|
|
364
|
+
self.assertEqual(set(result), {1, 2, 4, 5})
|
|
365
|
+
|
|
366
|
+
def test_symmetric_difference_of_disjoint_sets_is_union(
|
|
367
|
+
self: Self,
|
|
368
|
+
) -> None:
|
|
369
|
+
result: Any
|
|
370
|
+
value: MutableSetSlot[Any]
|
|
371
|
+
value = MutableSetSlot({1, 2})
|
|
372
|
+
result = value.symmetric_difference({3, 4})
|
|
373
|
+
self.assertEqual(set(result), {1, 2, 3, 4})
|
|
374
|
+
|
|
375
|
+
def test_symmetric_difference_of_equal_sets_is_empty(self: Self) -> None:
|
|
376
|
+
result: Any
|
|
377
|
+
value: MutableSetSlot[Any]
|
|
378
|
+
value = MutableSetSlot({1, 2})
|
|
379
|
+
result = value.symmetric_difference({1, 2})
|
|
380
|
+
self.assertEqual(set(result), set())
|
|
381
|
+
|
|
382
|
+
def test_symmetric_difference_does_not_modify_original(self: Self) -> None:
|
|
383
|
+
value: MutableSetSlot[Any]
|
|
384
|
+
value = MutableSetSlot({1, 2, 3})
|
|
385
|
+
value.symmetric_difference({3, 4})
|
|
386
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class TestSymmetricDifferenceUpdate(unittest.TestCase):
|
|
390
|
+
def test_symmetric_difference_update_excludes_shared_elements(
|
|
391
|
+
self: Self,
|
|
392
|
+
) -> None:
|
|
393
|
+
value: MutableSetSlot[Any]
|
|
394
|
+
value = MutableSetSlot({1, 2, 3})
|
|
395
|
+
value.symmetric_difference_update({3, 4, 5})
|
|
396
|
+
self.assertEqual(set(value), {1, 2, 4, 5})
|
|
397
|
+
|
|
398
|
+
def test_symmetric_difference_update_of_disjoint_sets_is_union(
|
|
399
|
+
self: Self,
|
|
400
|
+
) -> None:
|
|
401
|
+
value: MutableSetSlot[Any]
|
|
402
|
+
value = MutableSetSlot({1, 2})
|
|
403
|
+
value.symmetric_difference_update({3, 4})
|
|
404
|
+
self.assertEqual(set(value), {1, 2, 3, 4})
|
|
405
|
+
|
|
406
|
+
def test_symmetric_difference_update_with_equal_set_empties_set(
|
|
407
|
+
self: Self,
|
|
408
|
+
) -> None:
|
|
409
|
+
value: MutableSetSlot[Any]
|
|
410
|
+
value = MutableSetSlot({1, 2})
|
|
411
|
+
value.symmetric_difference_update({1, 2})
|
|
412
|
+
self.assertEqual(set(value), set())
|
|
413
|
+
|
|
414
|
+
def test_symmetric_difference_update_returns_none(self: Self) -> None:
|
|
415
|
+
value: MutableSetSlot[Any]
|
|
416
|
+
value = MutableSetSlot({1, 2})
|
|
417
|
+
result = value.symmetric_difference_update({2, 3})
|
|
418
|
+
self.assertIsNone(result)
|
|
419
|
+
self.assertEqual(set(value), {1, 3})
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class TestUnion(unittest.TestCase):
|
|
423
|
+
def test_union_combines_elements(self: Self) -> None:
|
|
424
|
+
value: MutableSetSlot[Any]
|
|
425
|
+
value = MutableSetSlot({1, 2})
|
|
426
|
+
result = value.union({2, 3})
|
|
427
|
+
self.assertEqual(set(result), {1, 2, 3})
|
|
428
|
+
|
|
429
|
+
def test_union_accepts_multiple_iterables(self: Self) -> None:
|
|
430
|
+
value: MutableSetSlot[Any]
|
|
431
|
+
value = MutableSetSlot({1})
|
|
432
|
+
result = value.union({2}, [3, 4])
|
|
433
|
+
self.assertEqual(set(result), {1, 2, 3, 4})
|
|
434
|
+
|
|
435
|
+
def test_union_without_arguments_returns_same_contents(self: Self) -> None:
|
|
436
|
+
value: MutableSetSlot[Any]
|
|
437
|
+
value = MutableSetSlot({1, 2})
|
|
438
|
+
result = value.union()
|
|
439
|
+
self.assertEqual(set(result), {1, 2})
|
|
440
|
+
self.assertIsNot(result, value)
|
|
441
|
+
|
|
442
|
+
def test_union_does_not_modify_original(self: Self) -> None:
|
|
443
|
+
value: MutableSetSlot[Any]
|
|
444
|
+
value = MutableSetSlot({1, 2})
|
|
445
|
+
value.union({3, 4})
|
|
446
|
+
self.assertEqual(set(value), {1, 2})
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class TestUpdate(unittest.TestCase):
|
|
450
|
+
def test_update_adds_elements_from_iterable(self: Self) -> None:
|
|
451
|
+
value: MutableSetSlot[Any]
|
|
452
|
+
value = MutableSetSlot({1, 2})
|
|
453
|
+
value.update({2, 3, 4})
|
|
454
|
+
self.assertEqual(set(value), {1, 2, 3, 4})
|
|
455
|
+
|
|
456
|
+
def test_update_accepts_multiple_iterables(self: Self) -> None:
|
|
457
|
+
value: MutableSetSlot[Any]
|
|
458
|
+
value = MutableSetSlot({1})
|
|
459
|
+
value.update({2}, [3, 4])
|
|
460
|
+
self.assertEqual(set(value), {1, 2, 3, 4})
|
|
461
|
+
|
|
462
|
+
def test_update_ignores_duplicate_elements(self: Self) -> None:
|
|
463
|
+
value: MutableSetSlot[Any]
|
|
464
|
+
value = MutableSetSlot({1, 2})
|
|
465
|
+
value.update([1, 1, 2, 2])
|
|
466
|
+
self.assertEqual(set(value), {1, 2})
|
|
467
|
+
|
|
468
|
+
def test_update_returns_none(self: Self) -> None:
|
|
469
|
+
value: MutableSetSlot[Any]
|
|
470
|
+
value = MutableSetSlot({1})
|
|
471
|
+
result = value.update({2, 3})
|
|
472
|
+
self.assertIsNone(result)
|
|
473
|
+
self.assertEqual(set(value), {1, 2, 3})
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
if __name__ == "__main__":
|
|
477
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|