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.
Files changed (39) hide show
  1. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/PKG-INFO +1 -1
  2. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/pyproject.toml +1 -1
  3. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold/__init__.py +18 -5
  4. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/PKG-INFO +1 -1
  5. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/SOURCES.txt +1 -0
  6. datahold-4.0.0.dev27/tests/test_MutableDictSlot.py +403 -0
  7. datahold-4.0.0.dev27/tests/test_MutableSetSlot.py +477 -0
  8. datahold-4.0.0.dev26/tests/test_MutableDictSlot.py +0 -0
  9. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/LICENSE.txt +0 -0
  10. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/MANIFEST.in +0 -0
  11. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/README.rst +0 -0
  12. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v1.1.rst +0 -0
  13. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v1.2.rst +0 -0
  14. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.0.rst +0 -0
  15. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.1.rst +0 -0
  16. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.2.rst +0 -0
  17. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v2.3.rst +0 -0
  18. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v3.0.rst +0 -0
  19. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/docs/v4.0.rst +0 -0
  20. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/run_tests.py +0 -0
  21. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/setup.cfg +0 -0
  22. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold/py.typed +0 -0
  23. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/dependency_links.txt +0 -0
  24. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/requires.txt +0 -0
  25. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/src/datahold.egg-info/top_level.txt +0 -0
  26. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_FrozenListSlot.py +0 -0
  27. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_MutableListSlot.py +0 -0
  28. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_abstractmethods.py +0 -0
  29. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_all.py +0 -0
  30. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_attributes.py +0 -0
  31. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_docs.py +0 -0
  32. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_generic.py +0 -0
  33. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_internal_comparison.py +0 -0
  34. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_issubclass.py +0 -0
  35. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_name.py +0 -0
  36. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_nested.py +0 -0
  37. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_sorted.py +0 -0
  38. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/test_variance.py +0 -0
  39. {datahold-4.0.0.dev26 → datahold-4.0.0.dev27}/tests/testdata.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev26
3
+ Version: 4.0.0.dev27
4
4
  Summary: This project wraps common mutable datastructures for inheritance with modification.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -31,7 +31,7 @@ license-files = [
31
31
  name = "datahold"
32
32
  readme = "README.rst"
33
33
  requires-python = ">=3.11"
34
- version = "4.0.0.dev26"
34
+ version = "4.0.0.dev27"
35
35
 
36
36
  [project.urls]
37
37
  Download = "https://pypi.org/project/datahold/#files"
@@ -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], /) -> Self:
264
- return type(self)(self.__frozen__().issubset(other)) # type: ignore
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], /) -> Self:
268
- return type(self)(self.__frozen__().issuperset(other)) # type: ignore
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)) # type: ignore
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev26
3
+ Version: 4.0.0.dev27
4
4
  Summary: This project wraps common mutable datastructures for inheritance with modification.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -22,6 +22,7 @@ src/datahold.egg-info/top_level.txt
22
22
  tests/test_FrozenListSlot.py
23
23
  tests/test_MutableDictSlot.py
24
24
  tests/test_MutableListSlot.py
25
+ tests/test_MutableSetSlot.py
25
26
  tests/test_abstractmethods.py
26
27
  tests/test_all.py
27
28
  tests/test_attributes.py
@@ -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