datahold 4.0.0.dev31__tar.gz → 4.0.0.dev32__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.dev31 → datahold-4.0.0.dev32}/PKG-INFO +1 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/pyproject.toml +1 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold/__init__.py +42 -24
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_FrozenDictSlot.py +0 -41
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_MutableDictSlot.py +321 -12
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_MutableListSlot.py +219 -180
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_MutableSetSlot.py +365 -32
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_abstractmethods.py +1 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_attributes.py +2 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_docs.py +4 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_generic.py +2 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_issubclass.py +8 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_metaclass.py +1 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_name.py +4 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_nested.py +15 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_sorted.py +2 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_variance.py +7 -1
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/testdata.toml +15 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/README.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/run_tests.py +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/setup.cfg +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold.egg-info/SOURCES.txt +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/src/datahold.egg-info/top_level.txt +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_FrozenListSlot.py +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_FrozenSetSlot.py +0 -0
- {datahold-4.0.0.dev31 → datahold-4.0.0.dev32}/tests/test_all.py +0 -0
|
@@ -89,15 +89,17 @@ class SupportsAddAndDiscard[Item: abc.Hashable](Protocol):
|
|
|
89
89
|
class Mapping_Frozen[Key, Value](Collection_Frozen[Key], Protocol):
|
|
90
90
|
@setdoc.basic
|
|
91
91
|
def __getitem__(self: Self, key: Never, /) -> Value: ...
|
|
92
|
+
|
|
93
|
+
|
|
92
94
|
class SupportsDelitemAndSetitem[Key, Value](
|
|
93
95
|
Protocol,
|
|
94
96
|
):
|
|
95
97
|
@setdoc.basic
|
|
96
|
-
def __delitem__(self: Self, key: Key
|
|
98
|
+
def __delitem__(self: Self, key: Key, /) -> object: ...
|
|
97
99
|
@setdoc.basic
|
|
98
|
-
def __setitem__(
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
def __setitem__(self: Self, key: Key, value: Value, /) -> object: ...
|
|
101
|
+
|
|
102
|
+
|
|
101
103
|
class Sequence_Frozen[Item](
|
|
102
104
|
Collection_Frozen[Item],
|
|
103
105
|
Protocol,
|
|
@@ -481,7 +483,7 @@ class Mapping[Key: abc.Hashable, Value](
|
|
|
481
483
|
|
|
482
484
|
|
|
483
485
|
class MutableMapping[Key: abc.Hashable, Value](
|
|
484
|
-
Mapping[Key, Value],
|
|
486
|
+
Mapping[Key | str, Value | None],
|
|
485
487
|
abc.MutableMapping[Key | str, Value | None],
|
|
486
488
|
MutableObject[
|
|
487
489
|
Mapping_Frozen[Key | str, Value | None],
|
|
@@ -509,8 +511,8 @@ class MutableMapping[Key: abc.Hashable, Value](
|
|
|
509
511
|
|
|
510
512
|
|
|
511
513
|
class DictLike[Key: abc.Hashable, Value](
|
|
512
|
-
Mapping[Key
|
|
513
|
-
abc.Reversible[Key
|
|
514
|
+
Mapping[Key, Value],
|
|
515
|
+
abc.Reversible[Key],
|
|
514
516
|
):
|
|
515
517
|
"""Provide abc for custom dict-like."""
|
|
516
518
|
|
|
@@ -518,7 +520,7 @@ class DictLike[Key: abc.Hashable, Value](
|
|
|
518
520
|
|
|
519
521
|
@abstractmethod
|
|
520
522
|
@setdoc.basic
|
|
521
|
-
def __frozen__(self: Self, /) ->
|
|
523
|
+
def __frozen__(self: Self, /) -> frozendict[Key, Value]: ...
|
|
522
524
|
|
|
523
525
|
@abstractmethod
|
|
524
526
|
@setdoc.basic
|
|
@@ -528,38 +530,54 @@ class DictLike[Key: abc.Hashable, Value](
|
|
|
528
530
|
/,
|
|
529
531
|
**kwargs: Value,
|
|
530
532
|
): ...
|
|
533
|
+
|
|
531
534
|
@setdoc.basic
|
|
532
535
|
def __or__[Key_: abc.Hashable, Value_](
|
|
533
536
|
self: Self,
|
|
534
|
-
other:
|
|
537
|
+
other: DictLike[Key_, Value_],
|
|
535
538
|
/,
|
|
536
539
|
) -> DictLike[Key | Key_, Value | Value_]:
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
540
|
+
if isinstance(other, DictLike):
|
|
541
|
+
return type(self)( # type: ignore[return-value]
|
|
542
|
+
self.__frozen__() | other # type: ignore[arg-type]
|
|
543
|
+
)
|
|
544
|
+
else:
|
|
541
545
|
return NotImplemented
|
|
542
|
-
return type(self)( # type: ignore[return-value]
|
|
543
|
-
self.__frozen__() | other_
|
|
544
|
-
)
|
|
545
546
|
|
|
546
547
|
@setdoc.basic
|
|
547
|
-
def __reversed__(self: Self, /) -> abc.Iterator[Key
|
|
548
|
+
def __reversed__(self: Self, /) -> abc.Iterator[Key]:
|
|
548
549
|
yield from reversed(self.__frozen__())
|
|
549
550
|
|
|
551
|
+
@overload
|
|
550
552
|
@classmethod
|
|
551
553
|
@setdoc.basic
|
|
552
554
|
def fromkeys(
|
|
553
555
|
cls: type[Self],
|
|
554
|
-
iterable: abc.Iterable[Key
|
|
555
|
-
value: Value | None = None,
|
|
556
|
+
iterable: abc.Iterable[Key],
|
|
556
557
|
/,
|
|
557
|
-
) ->
|
|
558
|
+
) -> DictLike[Key, None]: ...
|
|
559
|
+
@overload
|
|
560
|
+
@classmethod
|
|
561
|
+
@setdoc.basic
|
|
562
|
+
def fromkeys(
|
|
563
|
+
cls: type[Self],
|
|
564
|
+
iterable: abc.Iterable[Key],
|
|
565
|
+
value: Value,
|
|
566
|
+
/,
|
|
567
|
+
) -> Self: ...
|
|
568
|
+
@classmethod
|
|
569
|
+
@setdoc.basic
|
|
570
|
+
def fromkeys(
|
|
571
|
+
cls: type[Self],
|
|
572
|
+
iterable: abc.Iterable[Key],
|
|
573
|
+
value: Any = None,
|
|
574
|
+
/,
|
|
575
|
+
) -> Any:
|
|
558
576
|
return cls(dict.fromkeys(iterable, value))
|
|
559
577
|
|
|
560
578
|
|
|
561
579
|
class FrozenDictLike[Key: abc.Hashable, Value](
|
|
562
|
-
FrozenObjectLike[
|
|
580
|
+
FrozenObjectLike[frozendict[Key, Value]],
|
|
563
581
|
DictLike[Key, Value],
|
|
564
582
|
):
|
|
565
583
|
"""Provide abc for custom frozen dict-like."""
|
|
@@ -568,7 +586,7 @@ class FrozenDictLike[Key: abc.Hashable, Value](
|
|
|
568
586
|
|
|
569
587
|
|
|
570
588
|
class MutableDictLike[Key: abc.Hashable, Value](
|
|
571
|
-
DictLike[Key, Value],
|
|
589
|
+
DictLike[Key | str, Value | None],
|
|
572
590
|
MutableObjectLike[FrozenDict[Key, Value], Dict[Key, Value]],
|
|
573
591
|
MutableMapping[Key, Value],
|
|
574
592
|
):
|
|
@@ -590,7 +608,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
590
608
|
other: Dict_Init[Key, Value] = (),
|
|
591
609
|
/,
|
|
592
610
|
**kwargs: Value | None,
|
|
593
|
-
):
|
|
611
|
+
) -> None:
|
|
594
612
|
self.update(
|
|
595
613
|
other, # type: ignore[arg-type]
|
|
596
614
|
**kwargs,
|
|
@@ -619,7 +637,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
619
637
|
|
|
620
638
|
|
|
621
639
|
class FrozenDictSlot[Key: abc.Hashable, Value](
|
|
622
|
-
FrozenObjectSlot[
|
|
640
|
+
FrozenObjectSlot[frozendict[Key, Value]],
|
|
623
641
|
FrozenDictLike[Key, Value],
|
|
624
642
|
):
|
|
625
643
|
"""Provide slotted frozen dict-like."""
|
|
@@ -17,7 +17,6 @@ __all__: list[str] = [
|
|
|
17
17
|
"TestOperatorReversed",
|
|
18
18
|
"TestOperatorSubscription",
|
|
19
19
|
"TestOperatorTruth",
|
|
20
|
-
"TestOperatorUnion",
|
|
21
20
|
]
|
|
22
21
|
import unittest
|
|
23
22
|
from collections import abc
|
|
@@ -430,46 +429,6 @@ class TestOperatorInequality(unittest.TestCase):
|
|
|
430
429
|
self.assertFalse(value != {"a": 1})
|
|
431
430
|
|
|
432
431
|
|
|
433
|
-
class TestOperatorUnion(unittest.TestCase):
|
|
434
|
-
def test_disjoint_mappings(self: Self, /) -> None:
|
|
435
|
-
left: FrozenDictSlot[Any, Any]
|
|
436
|
-
result: Any
|
|
437
|
-
left = FrozenDictSlot({"a": 1})
|
|
438
|
-
result = left | {"b": 2}
|
|
439
|
-
self.assertEqual(result, {"a": 1, "b": 2})
|
|
440
|
-
|
|
441
|
-
def test_right_operand_overrides(self: Self, /) -> None:
|
|
442
|
-
left: FrozenDictSlot[Any, Any]
|
|
443
|
-
result: Any
|
|
444
|
-
left = FrozenDictSlot({"a": 1, "b": 2})
|
|
445
|
-
result = left | {"b": 20}
|
|
446
|
-
self.assertEqual(result, {"a": 1, "b": 20})
|
|
447
|
-
|
|
448
|
-
def test_union_with_empty_dictionary(self: Self, /) -> None:
|
|
449
|
-
left: FrozenDictSlot[Any, Any]
|
|
450
|
-
result: Any
|
|
451
|
-
left = FrozenDictSlot({"a": 1})
|
|
452
|
-
result = left | {}
|
|
453
|
-
self.assertEqual(result, {"a": 1})
|
|
454
|
-
|
|
455
|
-
def test_union_does_not_change_left_operand(self: Self, /) -> None:
|
|
456
|
-
answer: FrozenDictSlot[str, int]
|
|
457
|
-
left: FrozenDictSlot[str, int]
|
|
458
|
-
solution: FrozenDictSlot[str, int]
|
|
459
|
-
left = FrozenDictSlot({"a": 1})
|
|
460
|
-
answer = left | {"b": 2} # type: ignore[operator]
|
|
461
|
-
# this is the only mypy suppression
|
|
462
|
-
# that seemingly stems not from a faulty typeshed
|
|
463
|
-
# but from a fault in mypy itself
|
|
464
|
-
# FrozenDictSlot[str, int].__or__
|
|
465
|
-
# annotates SupportsKeyAndGetitem[str, int | None]
|
|
466
|
-
# which is covariant in the second parameter
|
|
467
|
-
# and therefore should work
|
|
468
|
-
solution = FrozenDictSlot({"a": 1, "b": 2})
|
|
469
|
-
self.assertEqual(answer, solution)
|
|
470
|
-
self.assertEqual(left, {"a": 1})
|
|
471
|
-
|
|
472
|
-
|
|
473
432
|
class TestOperatorLength(unittest.TestCase):
|
|
474
433
|
def test_empty_mapping(self: Self, /) -> None:
|
|
475
434
|
self.assertEqual(len(FrozenDictSlot({})), 0)
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestMethodClear",
|
|
3
|
+
"TestMethodCopy",
|
|
4
|
+
"TestMethodFromKeys",
|
|
5
|
+
"TestMethodGet",
|
|
6
|
+
"TestMethodItems",
|
|
7
|
+
"TestMethodKeys",
|
|
8
|
+
"TestMethodPop",
|
|
9
|
+
"TestMethodPopItem",
|
|
10
|
+
"TestMethodSetDefault",
|
|
11
|
+
"TestMethodUpdate",
|
|
12
|
+
"TestMethodValues",
|
|
13
|
+
"TestOperatorContains",
|
|
14
|
+
"TestOperatorDelItem",
|
|
15
|
+
"TestOperatorEqual",
|
|
16
|
+
"TestOperatorGetItem",
|
|
17
|
+
"TestOperatorInPlaceUnion",
|
|
18
|
+
"TestOperatorIteration",
|
|
19
|
+
"TestOperatorLength",
|
|
20
|
+
"TestOperatorNotEqual",
|
|
21
|
+
"TestOperatorReversed",
|
|
22
|
+
"TestOperatorReverseUnion",
|
|
23
|
+
"TestOperatorSetItem",
|
|
24
|
+
"TestOperatorTruthiness",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
1
28
|
import unittest
|
|
2
29
|
from collections import abc
|
|
3
30
|
from typing import Any, Self
|
|
@@ -5,7 +32,7 @@ from typing import Any, Self
|
|
|
5
32
|
from datahold import MutableDictSlot
|
|
6
33
|
|
|
7
34
|
|
|
8
|
-
class
|
|
35
|
+
class TestMethodClear(unittest.TestCase):
|
|
9
36
|
def test_clear_removes_all_entries(self: Self, /) -> None:
|
|
10
37
|
value: MutableDictSlot[Any, Any]
|
|
11
38
|
value = MutableDictSlot({"a": 1, "b": 2})
|
|
@@ -40,7 +67,7 @@ class TestClear(unittest.TestCase):
|
|
|
40
67
|
self.assertEqual(list(items_view), [])
|
|
41
68
|
|
|
42
69
|
|
|
43
|
-
class
|
|
70
|
+
class TestMethodCopy(unittest.TestCase):
|
|
44
71
|
def test_copy_has_equal_contents(self: Self, /) -> None:
|
|
45
72
|
copied: MutableDictSlot[Any, Any]
|
|
46
73
|
original: MutableDictSlot[Any, Any]
|
|
@@ -79,9 +106,9 @@ class TestCopy(unittest.TestCase):
|
|
|
79
106
|
self.assertEqual(copied, {"a": 2, "b": 3})
|
|
80
107
|
|
|
81
108
|
|
|
82
|
-
class
|
|
109
|
+
class TestMethodFromKeys(unittest.TestCase):
|
|
83
110
|
def test_fromkeys_uses_none_by_default(self: Self, /) -> None:
|
|
84
|
-
result:
|
|
111
|
+
result: Any
|
|
85
112
|
result = MutableDictSlot.fromkeys(["a", "b", "c"])
|
|
86
113
|
self.assertEqual(result, {"a": None, "b": None, "c": None})
|
|
87
114
|
self.assertEqual(list(result), ["a", "b", "c"])
|
|
@@ -109,7 +136,7 @@ class TestFromKeys(unittest.TestCase):
|
|
|
109
136
|
self.assertEqual(result["b"], [1])
|
|
110
137
|
|
|
111
138
|
|
|
112
|
-
class
|
|
139
|
+
class TestMethodGet(unittest.TestCase):
|
|
113
140
|
def test_get_returns_existing_value(self: Self, /) -> None:
|
|
114
141
|
value: MutableDictSlot[Any, Any]
|
|
115
142
|
value = MutableDictSlot({"a": 1})
|
|
@@ -140,7 +167,7 @@ class TestGet(unittest.TestCase):
|
|
|
140
167
|
self.assertEqual(value, {"a": 1})
|
|
141
168
|
|
|
142
169
|
|
|
143
|
-
class
|
|
170
|
+
class TestMethodItems(unittest.TestCase):
|
|
144
171
|
def test_items_contains_key_value_pairs(self: Self, /) -> None:
|
|
145
172
|
result: abc.Collection[Any]
|
|
146
173
|
value: MutableDictSlot[Any, Any]
|
|
@@ -180,7 +207,7 @@ class TestItems(unittest.TestCase):
|
|
|
180
207
|
self.assertNotIn(("missing", 1), result)
|
|
181
208
|
|
|
182
209
|
|
|
183
|
-
class
|
|
210
|
+
class TestMethodKeys(unittest.TestCase):
|
|
184
211
|
def test_keys_contains_all_keys(self: Self, /) -> None:
|
|
185
212
|
result: abc.Collection[Any]
|
|
186
213
|
value: MutableDictSlot[Any, Any]
|
|
@@ -220,7 +247,7 @@ class TestKeys(unittest.TestCase):
|
|
|
220
247
|
self.assertNotIn(1, result)
|
|
221
248
|
|
|
222
249
|
|
|
223
|
-
class
|
|
250
|
+
class TestMethodPop(unittest.TestCase):
|
|
224
251
|
def test_pop_returns_and_removes_existing_value(self: Self, /) -> None:
|
|
225
252
|
result: Any
|
|
226
253
|
value: MutableDictSlot[Any, Any]
|
|
@@ -256,7 +283,7 @@ class TestPop(unittest.TestCase):
|
|
|
256
283
|
self.assertEqual(value, {})
|
|
257
284
|
|
|
258
285
|
|
|
259
|
-
class
|
|
286
|
+
class TestMethodPopItem(unittest.TestCase):
|
|
260
287
|
def test_popitem_removes_last_inserted_pair(self: Self, /) -> None:
|
|
261
288
|
result: tuple[Any, Any]
|
|
262
289
|
value: MutableDictSlot[Any, Any]
|
|
@@ -295,7 +322,7 @@ class TestPopItem(unittest.TestCase):
|
|
|
295
322
|
self.assertEqual(value, {})
|
|
296
323
|
|
|
297
324
|
|
|
298
|
-
class
|
|
325
|
+
class TestMethodSetDefault(unittest.TestCase):
|
|
299
326
|
def test_setdefault_returns_existing_value(self: Self, /) -> None:
|
|
300
327
|
result: Any
|
|
301
328
|
value: MutableDictSlot[Any, Any]
|
|
@@ -335,7 +362,7 @@ class TestSetDefault(unittest.TestCase):
|
|
|
335
362
|
self.assertEqual(value["a"], [1])
|
|
336
363
|
|
|
337
364
|
|
|
338
|
-
class
|
|
365
|
+
class TestMethodUpdate(unittest.TestCase):
|
|
339
366
|
def test_update_from_mapping(self: Self, /) -> None:
|
|
340
367
|
value: MutableDictSlot[Any, Any]
|
|
341
368
|
value = MutableDictSlot({"a": 1})
|
|
@@ -363,7 +390,7 @@ class TestUpdate(unittest.TestCase):
|
|
|
363
390
|
self.assertEqual(value, {"a": 10, "b": 20, "c": 30})
|
|
364
391
|
|
|
365
392
|
|
|
366
|
-
class
|
|
393
|
+
class TestMethodValues(unittest.TestCase):
|
|
367
394
|
def test_values_contains_all_values(self: Self, /) -> None:
|
|
368
395
|
result: Any
|
|
369
396
|
value: MutableDictSlot[Any, Any]
|
|
@@ -400,5 +427,287 @@ class TestValues(unittest.TestCase):
|
|
|
400
427
|
self.assertEqual(result.count(1), 2)
|
|
401
428
|
|
|
402
429
|
|
|
430
|
+
class TestOperatorGetItem(unittest.TestCase):
|
|
431
|
+
def test_existing_key(self: Self, /) -> None:
|
|
432
|
+
slot: MutableDictSlot[Any, Any]
|
|
433
|
+
slot = MutableDictSlot({"name": "Alice"})
|
|
434
|
+
self.assertEqual(slot["name"], "Alice")
|
|
435
|
+
|
|
436
|
+
def test_missing_key_raises_key_error(self: Self, /) -> None:
|
|
437
|
+
slot: MutableDictSlot[Any, Any]
|
|
438
|
+
slot = MutableDictSlot({"name": "Alice"})
|
|
439
|
+
with self.assertRaises(KeyError):
|
|
440
|
+
_ = slot["missing"]
|
|
441
|
+
|
|
442
|
+
def test_none_value(self: Self, /) -> None:
|
|
443
|
+
slot: MutableDictSlot[Any, Any]
|
|
444
|
+
slot = MutableDictSlot({"value": None})
|
|
445
|
+
self.assertIsNone(slot["value"])
|
|
446
|
+
|
|
447
|
+
def test_unhashable_key_raises_type_error(self: Self, /) -> None:
|
|
448
|
+
slot: MutableDictSlot[Any, Any]
|
|
449
|
+
slot = MutableDictSlot()
|
|
450
|
+
with self.assertRaises(Exception):
|
|
451
|
+
_ = slot[["unhashable"]]
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class TestOperatorSetItem(unittest.TestCase):
|
|
455
|
+
def test_adds_new_key(self: Self, /) -> None:
|
|
456
|
+
slot: MutableDictSlot[Any, Any]
|
|
457
|
+
slot = MutableDictSlot()
|
|
458
|
+
slot["name"] = "Alice"
|
|
459
|
+
self.assertEqual(slot, {"name": "Alice"})
|
|
460
|
+
|
|
461
|
+
def test_replaces_existing_value(self: Self, /) -> None:
|
|
462
|
+
slot: MutableDictSlot[Any, Any]
|
|
463
|
+
slot = MutableDictSlot({"count": 1})
|
|
464
|
+
slot["count"] = 2
|
|
465
|
+
self.assertEqual(slot["count"], 2)
|
|
466
|
+
|
|
467
|
+
def test_accepts_none_value(self: Self, /) -> None:
|
|
468
|
+
slot: MutableDictSlot[Any, Any]
|
|
469
|
+
slot = MutableDictSlot()
|
|
470
|
+
slot["value"] = None
|
|
471
|
+
self.assertIn("value", slot)
|
|
472
|
+
self.assertIsNone(slot["value"])
|
|
473
|
+
|
|
474
|
+
def test_unhashable_key_raises_type_error(self: Self, /) -> None:
|
|
475
|
+
slot: MutableDictSlot[Any, Any]
|
|
476
|
+
slot = MutableDictSlot()
|
|
477
|
+
with self.assertRaises(TypeError):
|
|
478
|
+
slot[["unhashable"]] = "value"
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class TestOperatorDelItem(unittest.TestCase):
|
|
482
|
+
def test_deletes_existing_key(self: Self, /) -> None:
|
|
483
|
+
slot: MutableDictSlot[Any, Any]
|
|
484
|
+
slot = MutableDictSlot({"a": 1, "b": 2})
|
|
485
|
+
del slot["a"]
|
|
486
|
+
self.assertEqual(slot, {"b": 2})
|
|
487
|
+
|
|
488
|
+
def test_missing_key_raises_key_error(self: Self, /) -> None:
|
|
489
|
+
slot: MutableDictSlot[Any, Any]
|
|
490
|
+
slot = MutableDictSlot({"a": 1})
|
|
491
|
+
with self.assertRaises(KeyError):
|
|
492
|
+
del slot["missing"]
|
|
493
|
+
|
|
494
|
+
def test_delete_only_item_makes_empty(self: Self, /) -> None:
|
|
495
|
+
slot: MutableDictSlot[Any, Any]
|
|
496
|
+
slot = MutableDictSlot({"a": 1})
|
|
497
|
+
del slot["a"]
|
|
498
|
+
self.assertEqual(slot, {})
|
|
499
|
+
|
|
500
|
+
def test_unhashable_key_raises_type_error(self: Self, /) -> None:
|
|
501
|
+
slot: MutableDictSlot[Any, Any]
|
|
502
|
+
slot = MutableDictSlot({"a": 1})
|
|
503
|
+
with self.assertRaises(TypeError):
|
|
504
|
+
del slot[["unhashable"]]
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class TestOperatorContains(unittest.TestCase):
|
|
508
|
+
def test_existing_key_is_contained(self: Self, /) -> None:
|
|
509
|
+
slot: MutableDictSlot[Any, Any]
|
|
510
|
+
slot = MutableDictSlot({"a": 1})
|
|
511
|
+
self.assertIn("a", slot)
|
|
512
|
+
|
|
513
|
+
def test_missing_key_is_not_contained(self: Self, /) -> None:
|
|
514
|
+
slot: MutableDictSlot[Any, Any]
|
|
515
|
+
slot = MutableDictSlot({"a": 1})
|
|
516
|
+
self.assertNotIn("b", slot)
|
|
517
|
+
|
|
518
|
+
def test_values_are_not_checked(self: Self, /) -> None:
|
|
519
|
+
slot: MutableDictSlot[Any, Any]
|
|
520
|
+
slot = MutableDictSlot({"a": "value"})
|
|
521
|
+
self.assertNotIn("value", slot)
|
|
522
|
+
|
|
523
|
+
def test_unhashable_key_raises_type_error(self: Self, /) -> None:
|
|
524
|
+
slot: MutableDictSlot[Any, Any]
|
|
525
|
+
slot = MutableDictSlot({"a": 1})
|
|
526
|
+
self.assertFalse(["unhashable"] in slot)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
class TestOperatorEqual(unittest.TestCase):
|
|
530
|
+
def test_equal_contents(self: Self, /) -> None:
|
|
531
|
+
x: MutableDictSlot[str, int]
|
|
532
|
+
y: MutableDictSlot[str, int]
|
|
533
|
+
x = MutableDictSlot({"a": 1, "b": 2})
|
|
534
|
+
y = MutableDictSlot({"a": 1, "b": 2})
|
|
535
|
+
self.assertTrue(x == x)
|
|
536
|
+
self.assertTrue(x == y)
|
|
537
|
+
|
|
538
|
+
def test_order_does_not_affect_equality(self: Self, /) -> None:
|
|
539
|
+
x: MutableDictSlot[str, int]
|
|
540
|
+
y: MutableDictSlot[str, int]
|
|
541
|
+
x = MutableDictSlot({"a": 1, "b": 2})
|
|
542
|
+
y = MutableDictSlot({"b": 2, "a": 1})
|
|
543
|
+
self.assertTrue(x == y)
|
|
544
|
+
|
|
545
|
+
def test_equal_to_plain_dict(self: Self, /) -> None:
|
|
546
|
+
slot: MutableDictSlot[str, int]
|
|
547
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
548
|
+
self.assertTrue(slot == {"a": 1})
|
|
549
|
+
|
|
550
|
+
def test_different_values_are_not_equal(self: Self, /) -> None:
|
|
551
|
+
slot: MutableDictSlot[str, int]
|
|
552
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
553
|
+
self.assertFalse(slot == {"a": 2})
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
class TestOperatorNotEqual(unittest.TestCase):
|
|
557
|
+
def test_different_values(self: Self, /) -> None:
|
|
558
|
+
slot: MutableDictSlot[str, int]
|
|
559
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
560
|
+
self.assertTrue(slot != {"a": 2})
|
|
561
|
+
|
|
562
|
+
def test_different_keys(self: Self, /) -> None:
|
|
563
|
+
slot: MutableDictSlot[str, int]
|
|
564
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
565
|
+
self.assertTrue(slot != {"b": 1})
|
|
566
|
+
|
|
567
|
+
def test_different_lengths(self: Self, /) -> None:
|
|
568
|
+
slot: MutableDictSlot[str, int]
|
|
569
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
570
|
+
self.assertTrue(slot != {"a": 1, "b": 2})
|
|
571
|
+
|
|
572
|
+
def test_identical_contents_are_not_unequal(self: Self, /) -> None:
|
|
573
|
+
slot: MutableDictSlot[str, int]
|
|
574
|
+
slot = MutableDictSlot[str, int]({"a": 1})
|
|
575
|
+
self.assertFalse(slot != {"a": 1})
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class TestOperatorInPlaceUnion(unittest.TestCase):
|
|
579
|
+
def test_adds_distinct_keys(self: Self, /) -> None:
|
|
580
|
+
slot: MutableDictSlot[Any, Any]
|
|
581
|
+
slot = MutableDictSlot({"a": 1})
|
|
582
|
+
slot |= {"b": 2}
|
|
583
|
+
self.assertEqual(slot, {"a": 1, "b": 2})
|
|
584
|
+
|
|
585
|
+
def test_overrides_duplicate_key(self: Self, /) -> None:
|
|
586
|
+
slot: MutableDictSlot[Any, Any]
|
|
587
|
+
slot = MutableDictSlot({"a": 1})
|
|
588
|
+
slot |= {"a": 2}
|
|
589
|
+
self.assertEqual(slot, {"a": 2})
|
|
590
|
+
|
|
591
|
+
def test_preserves_object_identity(self: Self, /) -> None:
|
|
592
|
+
slot: MutableDictSlot[Any, Any]
|
|
593
|
+
slot = MutableDictSlot({"a": 1})
|
|
594
|
+
original_id = id(slot)
|
|
595
|
+
slot |= {"b": 2}
|
|
596
|
+
self.assertEqual(id(slot), original_id)
|
|
597
|
+
|
|
598
|
+
def test_accepts_iterable_of_pairs(self: Self, /) -> None:
|
|
599
|
+
slot: MutableDictSlot[Any, Any]
|
|
600
|
+
slot = MutableDictSlot({"a": 1})
|
|
601
|
+
slot |= [("b", 2), ("c", 3)]
|
|
602
|
+
self.assertEqual(slot, {"a": 1, "b": 2, "c": 3})
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
class TestOperatorIteration(unittest.TestCase):
|
|
606
|
+
def test_iterates_over_keys(self: Self, /) -> None:
|
|
607
|
+
slot: MutableDictSlot[str, int]
|
|
608
|
+
slot = MutableDictSlot({"a": 1, "b": 2})
|
|
609
|
+
self.assertEqual(list(slot), ["a", "b"])
|
|
610
|
+
|
|
611
|
+
def test_empty_slot_has_no_iterations(self: Self, /) -> None:
|
|
612
|
+
slot: MutableDictSlot[Any, Any]
|
|
613
|
+
slot = MutableDictSlot()
|
|
614
|
+
self.assertEqual(list(slot), [])
|
|
615
|
+
|
|
616
|
+
def test_iteration_preserves_insertion_order(self: Self, /) -> None:
|
|
617
|
+
slot: MutableDictSlot[Any, Any]
|
|
618
|
+
slot = MutableDictSlot()
|
|
619
|
+
slot["third"] = 3
|
|
620
|
+
slot["first"] = 1
|
|
621
|
+
slot["second"] = 2
|
|
622
|
+
self.assertEqual(list(slot), ["third", "first", "second"])
|
|
623
|
+
|
|
624
|
+
def test_size_change_during_iteration_raises_runtime_error(
|
|
625
|
+
self: Self, /
|
|
626
|
+
) -> None:
|
|
627
|
+
init: list[tuple[str, int]]
|
|
628
|
+
x: dict[str, int]
|
|
629
|
+
x_iter: abc.Iterator[str]
|
|
630
|
+
x_next: str
|
|
631
|
+
y: MutableDictSlot[str, int]
|
|
632
|
+
y_iter: abc.Iterator[str]
|
|
633
|
+
y_next: str
|
|
634
|
+
init = [("a", 1), ("b", 2)]
|
|
635
|
+
x = dict(init)
|
|
636
|
+
y = MutableDictSlot(init)
|
|
637
|
+
x_iter = iter(x)
|
|
638
|
+
y_iter = iter(y)
|
|
639
|
+
x_next = next(x_iter)
|
|
640
|
+
y_next = next(y_iter)
|
|
641
|
+
self.assertEqual(x_next, y_next)
|
|
642
|
+
x["c"] = 3
|
|
643
|
+
y["c"] = 3
|
|
644
|
+
self.assertListEqual(list(x), list(y))
|
|
645
|
+
self.assertListEqual(list("abc"), list(y))
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
class TestOperatorReversed(unittest.TestCase):
|
|
649
|
+
def test_reverses_key_order(self: Self, /) -> None:
|
|
650
|
+
slot: MutableDictSlot[Any, Any]
|
|
651
|
+
slot = MutableDictSlot({"a": 1, "b": 2, "c": 3})
|
|
652
|
+
self.assertEqual(list(reversed(slot)), ["c", "b", "a"])
|
|
653
|
+
|
|
654
|
+
def test_empty_slot_reverses_to_empty(self: Self, /) -> None:
|
|
655
|
+
slot: MutableDictSlot[Any, Any]
|
|
656
|
+
slot = MutableDictSlot()
|
|
657
|
+
self.assertEqual(list(reversed(slot)), [])
|
|
658
|
+
|
|
659
|
+
def test_single_key(self: Self, /) -> None:
|
|
660
|
+
slot: MutableDictSlot[Any, Any]
|
|
661
|
+
slot = MutableDictSlot({"a": 1})
|
|
662
|
+
self.assertEqual(list(reversed(slot)), ["a"])
|
|
663
|
+
|
|
664
|
+
def test_replacing_value_does_not_change_order(self: Self, /) -> None:
|
|
665
|
+
slot: MutableDictSlot[Any, Any]
|
|
666
|
+
slot = MutableDictSlot({"a": 1, "b": 2})
|
|
667
|
+
slot["a"] = 3
|
|
668
|
+
self.assertEqual(list(reversed(slot)), ["b", "a"])
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
class TestOperatorLength(unittest.TestCase):
|
|
672
|
+
def test_empty_length(self: Self, /) -> None:
|
|
673
|
+
self.assertEqual(len(MutableDictSlot()), 0)
|
|
674
|
+
|
|
675
|
+
def test_nonempty_length(self: Self, /) -> None:
|
|
676
|
+
x: MutableDictSlot[str, int]
|
|
677
|
+
x = MutableDictSlot({"a": 1, "b": 2})
|
|
678
|
+
self.assertEqual(len(x), 2)
|
|
679
|
+
|
|
680
|
+
def test_replacing_value_does_not_change_length(self: Self, /) -> None:
|
|
681
|
+
slot: MutableDictSlot[Any, Any]
|
|
682
|
+
slot = MutableDictSlot({"a": 1})
|
|
683
|
+
slot["a"] = 2
|
|
684
|
+
self.assertEqual(len(slot), 1)
|
|
685
|
+
|
|
686
|
+
def test_adding_and_deleting_changes_length(self: Self, /) -> None:
|
|
687
|
+
slot: MutableDictSlot[Any, Any]
|
|
688
|
+
slot = MutableDictSlot()
|
|
689
|
+
slot["a"] = 1
|
|
690
|
+
slot["b"] = 2
|
|
691
|
+
del slot["a"]
|
|
692
|
+
self.assertEqual(len(slot), 1)
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
class TestOperatorTruthiness(unittest.TestCase):
|
|
696
|
+
def test_empty_slot_is_false(self: Self, /) -> None:
|
|
697
|
+
self.assertFalse(MutableDictSlot())
|
|
698
|
+
|
|
699
|
+
def test_nonempty_slot_is_true(self: Self, /) -> None:
|
|
700
|
+
self.assertTrue(MutableDictSlot({"a": 1}))
|
|
701
|
+
|
|
702
|
+
def test_false_value_still_makes_slot_true(self: Self, /) -> None:
|
|
703
|
+
self.assertTrue(MutableDictSlot({"a": False}))
|
|
704
|
+
|
|
705
|
+
def test_deleting_last_key_makes_slot_false(self: Self, /) -> None:
|
|
706
|
+
slot: MutableDictSlot[Any, Any]
|
|
707
|
+
slot = MutableDictSlot({"a": 1})
|
|
708
|
+
del slot["a"]
|
|
709
|
+
self.assertFalse(slot)
|
|
710
|
+
|
|
711
|
+
|
|
403
712
|
if __name__ == "__main__":
|
|
404
713
|
unittest.main()
|