datahold 4.0.0.dev25__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 (38) hide show
  1. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/PKG-INFO +1 -1
  2. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/pyproject.toml +1 -1
  3. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold/__init__.py +43 -51
  4. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold.egg-info/PKG-INFO +1 -1
  5. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold.egg-info/SOURCES.txt +2 -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.dev25 → datahold-4.0.0.dev27}/tests/test_name.py +6 -11
  9. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_variance.py +1 -7
  10. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/testdata.toml +0 -17
  11. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/LICENSE.txt +0 -0
  12. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/MANIFEST.in +0 -0
  13. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/README.rst +0 -0
  14. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v1.1.rst +0 -0
  15. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v1.2.rst +0 -0
  16. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v2.0.rst +0 -0
  17. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v2.1.rst +0 -0
  18. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v2.2.rst +0 -0
  19. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v2.3.rst +0 -0
  20. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v3.0.rst +0 -0
  21. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/docs/v4.0.rst +0 -0
  22. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/run_tests.py +0 -0
  23. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/setup.cfg +0 -0
  24. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold/py.typed +0 -0
  25. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold.egg-info/dependency_links.txt +0 -0
  26. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold.egg-info/requires.txt +0 -0
  27. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/src/datahold.egg-info/top_level.txt +0 -0
  28. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_FrozenListSlot.py +0 -0
  29. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_MutableListSlot.py +0 -0
  30. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_abstractmethods.py +0 -0
  31. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_all.py +0 -0
  32. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_attributes.py +0 -0
  33. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_docs.py +0 -0
  34. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_generic.py +0 -0
  35. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_internal_comparison.py +0 -0
  36. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_issubclass.py +0 -0
  37. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_nested.py +0 -0
  38. {datahold-4.0.0.dev25 → datahold-4.0.0.dev27}/tests/test_sorted.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev25
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.dev25"
34
+ version = "4.0.0.dev27"
35
35
 
36
36
  [project.urls]
37
37
  Download = "https://pypi.org/project/datahold/#files"
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  __all__: list[str] = [
6
6
  "Collection",
7
7
  "DictLike",
8
- "DictSlot",
9
8
  "FrozenDictLike",
10
9
  "FrozenDictSlot",
11
10
  "FrozenListLike",
@@ -13,7 +12,6 @@ __all__: list[str] = [
13
12
  "FrozenSetLike",
14
13
  "FrozenSetSlot",
15
14
  "ListLike",
16
- "ListSlot",
17
15
  "Mapping",
18
16
  "MutableDictLike",
19
17
  "MutableDictSlot",
@@ -27,7 +25,6 @@ __all__: list[str] = [
27
25
  "Sequence",
28
26
  "Set",
29
27
  "SetLike",
30
- "SetSlot",
31
28
  ]
32
29
 
33
30
  from abc import abstractmethod
@@ -263,12 +260,12 @@ class SetLike[Item: abc.Hashable](Set[Item]):
263
260
  return type(self)(self.__frozen__().intersection(*others))
264
261
 
265
262
  @setdoc.basic
266
- def issubset(self: Self, other: abc.Iterable[abc.Hashable], /) -> Self:
267
- 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)
268
265
 
269
266
  @setdoc.basic
270
- def issuperset(self: Self, other: abc.Iterable[abc.Hashable], /) -> Self:
271
- 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)
272
269
 
273
270
  @setdoc.basic
274
271
  def symmetric_difference(self: Self, other: abc.Iterable[Item], /) -> Self:
@@ -276,7 +273,7 @@ class SetLike[Item: abc.Hashable](Set[Item]):
276
273
 
277
274
  @setdoc.basic
278
275
  def union(self: Self, /, *others: abc.Iterable[Item]) -> Self:
279
- return type(self)(self.__frozen__().union(others)) # type: ignore
276
+ return type(self)(self.__frozen__().union(*others))
280
277
 
281
278
 
282
279
  class FrozenSetLike[Item: abc.Hashable](SetLike[Item], abc.Hashable):
@@ -308,6 +305,11 @@ class MutableSetLike[Item: abc.Hashable](SetLike[Item], MutableSet[Item]):
308
305
  @abstractmethod
309
306
  @setdoc.basic
310
307
  def __mutate__(self: Self) -> ContextManager[__Mutable__[Item]]: ...
308
+
309
+ @setdoc.basic
310
+ def copy(self: Self, /) -> Self:
311
+ return type(self)(self)
312
+
311
313
  @setdoc.basic
312
314
  def difference_update(
313
315
  self: Self, /, *others: abc.Iterable[abc.Hashable]
@@ -338,32 +340,28 @@ class MutableSetLike[Item: abc.Hashable](SetLike[Item], MutableSet[Item]):
338
340
  ### SET-SLOT ###
339
341
 
340
342
 
341
- class SetSlot[Item: abc.Hashable](SetLike[Item]):
342
- """Provide slotted set-like."""
343
+ class FrozenSetSlot[Item](FrozenSetLike[Item]):
344
+ """Provide slotted frozen set-like."""
343
345
 
344
346
  __slots__ = ("_slot",)
345
347
 
346
- _slot: SetSlot.__Frozen__[Item]
348
+ _slot: FrozenSetSlot.__Frozen__[Item]
347
349
 
348
350
  @setdoc.basic
349
- def __frozen__(self: Self, /) -> SetSlot.__Frozen__[Item]:
351
+ def __frozen__(self: Self, /) -> FrozenSetSlot.__Frozen__[Item]:
350
352
  return self._slot
351
353
 
352
-
353
- class FrozenSetSlot[Item](SetSlot[Item], FrozenSetLike[Item]):
354
- """Provide slotted frozen set-like."""
355
-
356
- __slots__ = ()
357
-
358
354
  @setdoc.basic
359
355
  def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
360
356
  self._slot = frozenset(data)
361
357
 
362
358
 
363
- class MutableSetSlot[Item](SetSlot[Item], MutableSetLike[Item]):
359
+ class MutableSetSlot[Item](MutableSetLike[Item]):
364
360
  """Provide slotted mutable set-like."""
365
361
 
366
- __slots__ = ()
362
+ __slots__ = ("_slot",)
363
+
364
+ _slot: FrozenSetSlot.__Frozen__[Item]
367
365
 
368
366
  @contextmanager
369
367
  @setdoc.basic
@@ -433,9 +431,7 @@ class DictLike[Key: abc.Hashable, Value](
433
431
  """Provide abc for custom dict-like."""
434
432
 
435
433
  __slots__ = ()
436
- type __Frozen__[FrozenKey, FrozenValue] = frozendict[
437
- FrozenKey | str, Optional[FrozenValue]
438
- ]
434
+ type __Frozen__[Key_, Value_] = frozendict[Key_ | str, Optional[Value_]]
439
435
 
440
436
  @abstractmethod
441
437
  @setdoc.basic
@@ -534,31 +530,32 @@ class MutableDictLike[Key: abc.Hashable, Value](
534
530
  def copy(self: Self, /) -> Self:
535
531
  return type(self)(self)
536
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
+
537
541
 
538
542
  ### DICT-SLOT ###
539
543
 
540
544
 
541
- class DictSlot[Key: abc.Hashable, Value](DictLike[Key, Value]):
542
- """Provide slotted dict-like."""
545
+ class FrozenDictSlot[Key: abc.Hashable, Value](
546
+ FrozenDictLike[Key, Value],
547
+ ):
548
+ """Provide slotted frozen dict-like."""
543
549
 
544
550
  __slots__ = ("_slot",)
545
- _slot: DictSlot.__Frozen__[Key, Value]
551
+ _slot: FrozenDictSlot.__Frozen__[Key, Value]
546
552
 
547
553
  @setdoc.basic
548
554
  def __frozen__( # type: ignore[override]
549
555
  self: Self,
550
- ) -> DictSlot.__Frozen__[Key, Value]:
556
+ ) -> FrozenDictSlot.__Frozen__[Key, Value]:
551
557
  return self._slot
552
558
 
553
-
554
- class FrozenDictSlot[Key: abc.Hashable, Value](
555
- DictSlot[Key, Value],
556
- FrozenDictLike[Key, Value],
557
- ):
558
- """Provide slotted frozen dict-like."""
559
-
560
- __slots__ = ()
561
-
562
559
  @setdoc.basic
563
560
  def __init__(
564
561
  self: Self,
@@ -566,16 +563,16 @@ class FrozenDictSlot[Key: abc.Hashable, Value](
566
563
  /,
567
564
  **kwargs: Optional[Value],
568
565
  ) -> None:
569
- self._slot = frozendict(data) # type: ignore[arg-type]
566
+ self._slot = frozendict(data, **kwargs) # type: ignore[arg-type]
570
567
 
571
568
 
572
569
  class MutableDictSlot[Key: abc.Hashable, Value](
573
- DictSlot[Key, Value],
574
570
  MutableDictLike[Key, Value],
575
571
  ):
576
572
  """Provide slotted mutable dict-like."""
577
573
 
578
- __slots__ = ()
574
+ __slots__ = ("_slot",)
575
+ _slot: FrozenDictSlot.__Frozen__[Key, Value]
579
576
 
580
577
  @contextmanager
581
578
  @setdoc.basic
@@ -860,31 +857,26 @@ class MutableListLike[Item](ListLike[Item], MutableSequence[Item]):
860
857
  ### LIST-SLOT ###
861
858
 
862
859
 
863
- class ListSlot[Item](ListLike[Item]):
864
- """Provide slotted list-like class."""
860
+ class FrozenListSlot[Item](FrozenListLike[Item]):
861
+ """Provide slotted frozen list-like."""
865
862
 
866
863
  __slots__ = ("_slot",)
867
- _slot: ListSlot.__Frozen__[Item]
864
+ _slot: FrozenListSlot.__Frozen__[Item]
868
865
 
869
866
  @setdoc.basic
870
- def __frozen__(self: Self) -> ListSlot.__Frozen__[Item]:
867
+ def __frozen__(self: Self) -> FrozenListSlot.__Frozen__[Item]:
871
868
  return self._slot
872
869
 
873
-
874
- class FrozenListSlot[Item](ListSlot[Item], FrozenListLike[Item]):
875
- """Provide slotted frozen list-like."""
876
-
877
- __slots__ = ()
878
-
879
870
  @setdoc.basic
880
871
  def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
881
872
  self._slot = tuple(data)
882
873
 
883
874
 
884
- class MutableListSlot[Item](ListSlot[Item], MutableListLike[Item]):
875
+ class MutableListSlot[Item](MutableListLike[Item]):
885
876
  """Provide slotted mutable list-like."""
886
877
 
887
- __slots__ = ()
878
+ __slots__ = ("_slot",)
879
+ _slot: MutableListSlot.__Frozen__[Item]
888
880
 
889
881
  @contextmanager
890
882
  @setdoc.basic
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev25
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
@@ -20,7 +20,9 @@ src/datahold.egg-info/dependency_links.txt
20
20
  src/datahold.egg-info/requires.txt
21
21
  src/datahold.egg-info/top_level.txt
22
22
  tests/test_FrozenListSlot.py
23
+ tests/test_MutableDictSlot.py
23
24
  tests/test_MutableListSlot.py
25
+ tests/test_MutableSetSlot.py
24
26
  tests/test_abstractmethods.py
25
27
  tests/test_all.py
26
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()
@@ -40,17 +40,15 @@ class TestName(unittest.TestCase):
40
40
  self.assertTrue(issubclass(datatype, abc.Hashable))
41
41
  self.assertFalse(hasattr(datatype, "__Mutable__"))
42
42
  self.assertFalse(hasattr(datatype, "__mutate__"))
43
- self.assertTrue(
44
- issubclass(datatype, Lazy.get_import("datahold." + name[6:]))
45
- )
43
+ ancestor = getattr(datahold, name[6:], object)
44
+ self.assertTrue(issubclass(datatype, ancestor))
46
45
  # mutable
47
46
  if prefix == "Mutable":
48
47
  self.assertIn(datatype.__hash__, [None, object.__hash__])
49
48
  self.assertTrue(hasattr(datatype, "__Mutable__"))
50
49
  self.assertTrue(hasattr(datatype, "__mutate__"))
51
- self.assertTrue(
52
- issubclass(datatype, Lazy.get_import("datahold." + name[7:]))
53
- )
50
+ ancestor = getattr(datahold, name[7:], object)
51
+ self.assertTrue(issubclass(datatype, ancestor))
54
52
  # no prefix
55
53
  if prefix == "":
56
54
  self.assertIn(datatype.__hash__, [None, object.__hash__])
@@ -62,11 +60,8 @@ class TestName(unittest.TestCase):
62
60
  # slot
63
61
  if suffix == "Slot":
64
62
  self.assertTrue(hasattr(datatype, "__init__"))
65
- self.assertTrue(
66
- issubclass(
67
- datatype, Lazy.get_import("datahold." + name[:-4] + "Like")
68
- )
69
- )
63
+ ancestor = getattr(datahold, name[:-4] + "Like")
64
+ self.assertTrue(issubclass(datatype, ancestor))
70
65
  # no suffix
71
66
  if suffix == "":
72
67
  self.assertIs(datatype.__init__, object.__init__)
@@ -2,7 +2,7 @@
2
2
 
3
3
  import unittest
4
4
 
5
- from datahold import FrozenListLike, FrozenListSlot, ListLike, ListSlot
5
+ from datahold import FrozenListLike, FrozenListSlot, ListLike
6
6
 
7
7
 
8
8
  class Animal:
@@ -26,12 +26,6 @@ def cast_FrozenListLikeAnimal(
26
26
  return x
27
27
 
28
28
 
29
- def cast_ListSlotAnimal(
30
- x: ListSlot[Bear],
31
- ) -> ListSlot[Animal]:
32
- return x
33
-
34
-
35
29
  def cast_FrozenListSlotAnimal(
36
30
  x: FrozenListSlot[Bear],
37
31
  ) -> FrozenListSlot[Animal]:
@@ -7,10 +7,6 @@ __or__ = "datahold.DictLike"
7
7
  "collections.abc.Mapping" = true
8
8
  "datahold.Mapping" = true
9
9
 
10
- [datatypes.DictSlot.issubclass]
11
- "collections.abc.Mapping" = true
12
- "datahold.DictLike" = true
13
-
14
10
  [datatypes.FrozenDictLike.issubclass]
15
11
  "collections.abc.Hashable" = true
16
12
  "datahold.Mapping" = true
@@ -182,13 +178,6 @@ __Mutable__ = ""
182
178
  [datatypes.ListLike.varia]
183
179
  generic = 1
184
180
 
185
- [datatypes.ListSlot.nested]
186
- __Frozen__ = "builtins.tuple"
187
- __Mutable__ = ""
188
-
189
- [datatypes.ListSlot.varia]
190
- generic = 1
191
-
192
181
  [datatypes.Mapping.issubclass]
193
182
  "collections.abc.Mapping" = true
194
183
  "datahold.Collection" = true
@@ -288,7 +277,6 @@ sort = ""
288
277
  "datahold.Collection" = true
289
278
  "datahold.MutableSet" = true
290
279
  "datahold.MutableSetLike" = true
291
- "datahold.SetSlot" = true
292
280
 
293
281
  [datatypes.Sequence]
294
282
 
@@ -302,9 +290,4 @@ sort = ""
302
290
  "datahold.Set" = true
303
291
  "datahold.SetLike" = true
304
292
 
305
- [datatypes.SetSlot.issubclass]
306
- "collections.abc.Set" = true
307
- "datahold.Collection" = true
308
- "datahold.SetLike" = true
309
-
310
293
  [non_datatypes]
File without changes