datahold 4.0.0.dev28__tar.gz → 4.0.0.dev29__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 (40) hide show
  1. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/PKG-INFO +1 -1
  2. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/pyproject.toml +1 -1
  3. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold/__init__.py +79 -48
  4. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold.egg-info/PKG-INFO +1 -1
  5. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold.egg-info/SOURCES.txt +1 -0
  6. datahold-4.0.0.dev29/tests/test_FrozenDictSlot.py +271 -0
  7. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_FrozenSetSlot.py +243 -0
  8. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_MutableDictSlot.py +1 -2
  9. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/testdata.toml +4 -0
  10. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/LICENSE.txt +0 -0
  11. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/MANIFEST.in +0 -0
  12. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/README.rst +0 -0
  13. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v1.1.rst +0 -0
  14. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v1.2.rst +0 -0
  15. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v2.0.rst +0 -0
  16. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v2.1.rst +0 -0
  17. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v2.2.rst +0 -0
  18. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v2.3.rst +0 -0
  19. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v3.0.rst +0 -0
  20. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/docs/v4.0.rst +0 -0
  21. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/run_tests.py +0 -0
  22. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/setup.cfg +0 -0
  23. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold/py.typed +0 -0
  24. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold.egg-info/dependency_links.txt +0 -0
  25. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold.egg-info/requires.txt +0 -0
  26. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/src/datahold.egg-info/top_level.txt +0 -0
  27. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_FrozenListSlot.py +0 -0
  28. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_MutableListSlot.py +0 -0
  29. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_MutableSetSlot.py +0 -0
  30. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_abstractmethods.py +0 -0
  31. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_all.py +0 -0
  32. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_attributes.py +0 -0
  33. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_docs.py +0 -0
  34. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_generic.py +0 -0
  35. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_issubclass.py +0 -0
  36. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_metaclass.py +0 -0
  37. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_name.py +0 -0
  38. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_nested.py +0 -0
  39. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_sorted.py +0 -0
  40. {datahold-4.0.0.dev28 → datahold-4.0.0.dev29}/tests/test_variance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev28
3
+ Version: 4.0.0.dev29
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.dev28"
34
+ version = "4.0.0.dev29"
35
35
 
36
36
  [project.urls]
37
37
  Download = "https://pypi.org/project/datahold/#files"
@@ -9,6 +9,7 @@ __all__: list[str] = [
9
9
  "FrozenDictSlot",
10
10
  "FrozenListLike",
11
11
  "FrozenListSlot",
12
+ "FrozenObjectLike",
12
13
  "FrozenObjectSlot",
13
14
  "FrozenSetLike",
14
15
  "FrozenSetSlot",
@@ -95,7 +96,7 @@ class MutableSet_Mutable[Item: abc.Hashable](Protocol):
95
96
 
96
97
  class Mapping_Frozen[Key, Value](Collection_Frozen[Key], Protocol):
97
98
  @setdoc.basic
98
- def __getitem__(self: Self, key: abc.Hashable, /) -> Value: ...
99
+ def __getitem__(self: Self, key: Never, /) -> Value: ...
99
100
  class MutableMapping_Mutable[Key_, Value_](
100
101
  Protocol,
101
102
  ):
@@ -171,7 +172,7 @@ class MutableSequence_Mutable[Item](Protocol):
171
172
 
172
173
  type Dict[Key, Value] = dict[Key | str, Optional[Value]]
173
174
 
174
- type DictInit[Key, Value] = (
175
+ type Dict_Init[Key, Value] = (
175
176
  SupportsKeysAndGetitem[Key | str, Optional[Value]]
176
177
  | abc.Iterable[tuple[Key | str, Optional[Value]]]
177
178
  )
@@ -205,6 +206,19 @@ class MutableObject[Frozen, Mutable](Object[Frozen]):
205
206
  ### OBJECT-LIKE ###
206
207
 
207
208
 
209
+ class FrozenObjectLike[Frozen: abc.Hashable](
210
+ Object[Frozen],
211
+ abc.Hashable,
212
+ ):
213
+ """Provide abc for custom frozen object-like."""
214
+
215
+ __slots__ = ()
216
+
217
+ @setdoc.basic
218
+ def __hash__(self: Self, /) -> int:
219
+ return hash(self.__frozen__())
220
+
221
+
208
222
  class MutableObjectLike[Frozen, Mutable](
209
223
  MutableObject[Frozen, Mutable],
210
224
  ):
@@ -232,7 +246,8 @@ class ObjectSlot[Frozen](
232
246
  _slot: Frozen
233
247
 
234
248
 
235
- class FrozenObjectSlot[Frozen](
249
+ class FrozenObjectSlot[Frozen: abc.Hashable](
250
+ FrozenObjectLike[Frozen],
236
251
  ObjectSlot[Frozen],
237
252
  ):
238
253
  """Provide slotted abc for custom frozen object-like."""
@@ -314,7 +329,7 @@ class SetLike[Item: abc.Hashable](Set[Item]):
314
329
  def __frozen__(self: Self, /) -> frozenset[Item]: ...
315
330
  @abstractmethod
316
331
  @setdoc.basic
317
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None: ...
332
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None: ...
318
333
  @setdoc.basic
319
334
  def difference(self: Self, /, *others: abc.Iterable[abc.Hashable]) -> Self:
320
335
  return type(self)(self.__frozen__().difference(*others))
@@ -342,15 +357,14 @@ class SetLike[Item: abc.Hashable](Set[Item]):
342
357
  return type(self)(self.__frozen__().union(*others))
343
358
 
344
359
 
345
- class FrozenSetLike[Item: abc.Hashable](SetLike[Item], abc.Hashable):
360
+ class FrozenSetLike[Item: abc.Hashable](
361
+ FrozenObjectLike[frozenset[Item]],
362
+ SetLike[Item],
363
+ ):
346
364
  """Provide abc for custom frozen set-like."""
347
365
 
348
366
  __slots__ = ()
349
367
 
350
- @setdoc.basic
351
- def __hash__(self: Self, /) -> int:
352
- return hash(self.__frozen__())
353
-
354
368
 
355
369
  class MutableSetLike[Item: abc.Hashable](
356
370
  SetLike[Item],
@@ -367,9 +381,9 @@ class MutableSetLike[Item: abc.Hashable](
367
381
  return frozenset(mutable)
368
382
 
369
383
  @setdoc.basic
370
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
384
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
371
385
  with self.__mutate__() as mutable:
372
- mutable.update(data)
386
+ mutable.update(other)
373
387
 
374
388
  @setdoc.basic
375
389
  def difference_update(
@@ -410,8 +424,8 @@ class FrozenSetSlot[Item](
410
424
  __slots__ = ()
411
425
 
412
426
  @setdoc.basic
413
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
414
- self._slot = frozenset(data)
427
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
428
+ self._slot = frozenset(other)
415
429
 
416
430
 
417
431
  class MutableSetSlot[Item](
@@ -428,10 +442,10 @@ class MutableSetSlot[Item](
428
442
  self: Self,
429
443
  /,
430
444
  ) -> abc.Generator[set[Item], None, None]:
431
- slot: set[Item]
432
- slot = set(getattr(self, "_slot", ()))
433
- yield slot
434
- self._slot = frozenset(slot)
445
+ mutable: set[Item]
446
+ mutable = set(getattr(self, "_slot", ()))
447
+ yield mutable
448
+ self._slot = frozenset(mutable)
435
449
 
436
450
 
437
451
  ### MAPPING ###
@@ -449,8 +463,26 @@ class Mapping[Key: abc.Hashable, Value](
449
463
  @setdoc.basic
450
464
  def __frozen__(self: Self, /) -> Mapping_Frozen[Key, Value]: ...
451
465
  @setdoc.basic
452
- def __getitem__(self: Self, key: abc.Hashable, /) -> Value:
453
- return self.__frozen__()[key]
466
+ def __getitem__(self: Self, key: object, /) -> Value:
467
+ try:
468
+ return self.__frozen__()[key] # type: ignore[index]
469
+ except TypeError:
470
+ raise KeyError(key) from None
471
+
472
+ @overload
473
+ def get(self, key: object) -> Optional[Value]: ...
474
+ @overload
475
+ def get[Value_](self, key: object, default: Value_) -> Value | Value_: ...
476
+ @setdoc.basic
477
+ def get[Value_](
478
+ self: Self, key: object, default: Optional[Value_] = None
479
+ ) -> Optional[Value | Value_]:
480
+ # this method is just as Mapping.get
481
+ # with the signature corrected
482
+ try:
483
+ return self[key]
484
+ except KeyError:
485
+ return default
454
486
 
455
487
 
456
488
  class MutableMapping[Key: abc.Hashable, Value](
@@ -487,21 +519,20 @@ class DictLike[Key: abc.Hashable, Value](
487
519
 
488
520
  @abstractmethod
489
521
  @setdoc.basic
490
- def __frozen__(self: Self, /) -> FrozenDict[Key, Value]: # type: ignore[override]
491
- ...
522
+ def __frozen__(self: Self, /) -> FrozenDict[Key, Value]: ...
492
523
 
493
524
  @abstractmethod
494
525
  @setdoc.basic
495
526
  def __init__(
496
527
  self: Self,
497
- data: DictInit[Key, Value] = (),
528
+ other: Dict_Init[Key, Value] = (),
498
529
  /,
499
530
  **kwargs: Value,
500
531
  ): ...
501
532
  @setdoc.basic
502
533
  def __or__[Key_, Value_](
503
534
  self: Self,
504
- other: DictInit[Key_, Value_],
535
+ other: Dict_Init[Key_, Value_],
505
536
  /,
506
537
  ) -> DictLike[Key | Key_, Value | Value_]:
507
538
  data: FrozenDict[Key, Value]
@@ -525,17 +556,13 @@ class DictLike[Key: abc.Hashable, Value](
525
556
 
526
557
 
527
558
  class FrozenDictLike[Key: abc.Hashable, Value](
559
+ FrozenObjectLike[FrozenDict[Key, Value]],
528
560
  DictLike[Key, Value],
529
- abc.Hashable,
530
561
  ):
531
562
  """Provide abc for custom frozen dict-like."""
532
563
 
533
564
  __slots__ = ()
534
565
 
535
- @setdoc.basic
536
- def __hash__(self: Self, /) -> int:
537
- return hash(self.__frozen__())
538
-
539
566
 
540
567
  class MutableDictLike[Key: abc.Hashable, Value](
541
568
  DictLike[Key, Value],
@@ -547,7 +574,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
547
574
  __slots__ = ()
548
575
 
549
576
  @setdoc.basic
550
- def __frozen__( # type: ignore[override]
577
+ def __frozen__(
551
578
  self: Self,
552
579
  /,
553
580
  ) -> FrozenDict[Key, Value]:
@@ -557,16 +584,16 @@ class MutableDictLike[Key: abc.Hashable, Value](
557
584
  @setdoc.basic
558
585
  def __init__(
559
586
  self: Self,
560
- data: DictInit[Key, Value] = (),
587
+ other: Dict_Init[Key, Value] = (),
561
588
  /,
562
589
  **kwargs: Optional[Value],
563
590
  ):
564
- self.update(data, **kwargs)
591
+ self.update(other, **kwargs)
565
592
 
566
593
  @setdoc.basic
567
594
  def __ior__( # type: ignore[override]
568
595
  self: Self,
569
- other: DictInit[Key, Value],
596
+ other: Dict_Init[Key, Value],
570
597
  /,
571
598
  ) -> Self:
572
599
  with self.__mutate__() as mutable:
@@ -585,7 +612,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
585
612
  ### DICT-SLOT ###
586
613
 
587
614
 
588
- class FrozenDictSlot[Key: abc.Hashable, Value]( # type: ignore[misc]
615
+ class FrozenDictSlot[Key: abc.Hashable, Value](
589
616
  FrozenObjectSlot[FrozenDict[Key, Value]],
590
617
  FrozenDictLike[Key, Value],
591
618
  ):
@@ -596,11 +623,11 @@ class FrozenDictSlot[Key: abc.Hashable, Value]( # type: ignore[misc]
596
623
  @setdoc.basic
597
624
  def __init__(
598
625
  self: Self,
599
- data: DictInit[Key, Value] = (),
626
+ other: Dict_Init[Key, Value] = (),
600
627
  /,
601
628
  **kwargs: Optional[Value],
602
629
  ) -> None:
603
- self._slot = frozendict(data, **kwargs) # type: ignore[arg-type]
630
+ self._slot = frozendict(other, **kwargs) # type: ignore[arg-type]
604
631
 
605
632
 
606
633
  class MutableDictSlot[Key: abc.Hashable, Value](
@@ -617,10 +644,10 @@ class MutableDictSlot[Key: abc.Hashable, Value](
617
644
  self: Self,
618
645
  /,
619
646
  ) -> abc.Generator[Dict[Key, Value], None, None]:
620
- slot: Dict[Key, Value]
621
- slot = dict(getattr(self, "_slot", ()))
622
- yield slot
623
- self._slot = frozendict(slot)
647
+ mutable: Dict[Key, Value]
648
+ mutable = dict(getattr(self, "_slot", ()))
649
+ yield mutable
650
+ self._slot = frozendict(mutable)
624
651
 
625
652
 
626
653
  ### SEQUENCE ###
@@ -769,7 +796,7 @@ class ListLike[Item](Sequence[Item]):
769
796
 
770
797
  @abstractmethod
771
798
  @setdoc.basic
772
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None: ...
799
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None: ...
773
800
 
774
801
  @setdoc.basic
775
802
  def __le__(self: Self, other: ListLike[Any], /) -> bool:
@@ -799,7 +826,10 @@ class ListLike[Item](Sequence[Item]):
799
826
  return f"{type(self).__name__}({list(self)!r})"
800
827
 
801
828
 
802
- class FrozenListLike[Item](ListLike[Item], abc.Hashable):
829
+ class FrozenListLike[Item](
830
+ FrozenObjectLike[tuple[Item, ...]],
831
+ ListLike[Item],
832
+ ):
803
833
  """Provide abc for custom frozen list-like."""
804
834
 
805
835
  __slots__ = ()
@@ -843,9 +873,9 @@ class MutableListLike[Item](
843
873
  return self
844
874
 
845
875
  @setdoc.basic
846
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
876
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
847
877
  with self.__mutate__() as mutable:
848
- mutable.extend(data)
878
+ mutable.extend(other)
849
879
 
850
880
  @overload
851
881
  @setdoc.basic
@@ -894,8 +924,8 @@ class FrozenListSlot[Item](
894
924
  __slots__ = ()
895
925
 
896
926
  @setdoc.basic
897
- def __init__(self: Self, data: abc.Iterable[Item] = (), /) -> None:
898
- self._slot = tuple(data)
927
+ def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
928
+ self._slot = tuple(other)
899
929
 
900
930
 
901
931
  class MutableListSlot[Item](
@@ -909,6 +939,7 @@ class MutableListSlot[Item](
909
939
  @contextmanager
910
940
  @setdoc.basic
911
941
  def __mutate__(self: Self, /) -> abc.Generator[list[Item], None, None]:
912
- slot = list(getattr(self, "_slot", ()))
913
- yield slot
914
- self._slot = tuple(slot)
942
+ mutable: list[Item]
943
+ mutable = list(getattr(self, "_slot", ()))
944
+ yield mutable
945
+ self._slot = tuple(mutable)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev28
3
+ Version: 4.0.0.dev29
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
@@ -19,6 +19,7 @@ src/datahold.egg-info/SOURCES.txt
19
19
  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
+ tests/test_FrozenDictSlot.py
22
23
  tests/test_FrozenListSlot.py
23
24
  tests/test_FrozenSetSlot.py
24
25
  tests/test_MutableDictSlot.py
@@ -0,0 +1,271 @@
1
+ import unittest
2
+ from collections import abc
3
+ from typing import Any, Self
4
+
5
+ from datahold import FrozenDictSlot
6
+
7
+
8
+ class TestFromKeysMethod(unittest.TestCase):
9
+ def test_fromkeys_uses_none_by_default(self: Self) -> None:
10
+ result: Any = FrozenDictSlot.fromkeys(["a", "b", "c"])
11
+
12
+ self.assertEqual(result, {"a": None, "b": None, "c": None})
13
+
14
+ def test_fromkeys_uses_provided_value(self: Self) -> None:
15
+ result: Any = FrozenDictSlot.fromkeys(["a", "b"], 10)
16
+
17
+ self.assertEqual(result, {"a": 10, "b": 10})
18
+
19
+ def test_fromkeys_uses_same_value_object_for_every_key(self: Self) -> None:
20
+ value: Any = []
21
+ result: Any = FrozenDictSlot.fromkeys(["a", "b"], value)
22
+
23
+ self.assertIs(result["a"], value)
24
+ self.assertIs(result["b"], value)
25
+
26
+ def test_fromkeys_removes_duplicate_keys(self: Self) -> None:
27
+ result: Any = FrozenDictSlot.fromkeys(
28
+ ["a", "b", "a", "c", "b"],
29
+ 1,
30
+ )
31
+
32
+ self.assertEqual(list(result.keys()), ["a", "b", "c"])
33
+ self.assertEqual(result, {"a": 1, "b": 1, "c": 1})
34
+
35
+ def test_fromkeys_accepts_generator(self: Self) -> None:
36
+ keys: Any = (str(number) for number in range(3))
37
+ result: Any = FrozenDictSlot.fromkeys(keys, "value")
38
+
39
+ self.assertEqual(
40
+ result,
41
+ {"0": "value", "1": "value", "2": "value"},
42
+ )
43
+
44
+ def test_fromkeys_accepts_empty_iterable(self: Self) -> None:
45
+ self.assertEqual(FrozenDictSlot.fromkeys([], 1), {})
46
+
47
+ def test_fromkeys_returns_frozen_dict_slot(self: Self) -> None:
48
+ result: Any = FrozenDictSlot.fromkeys(["a"], 1)
49
+
50
+ self.assertIsInstance(result, FrozenDictSlot)
51
+
52
+ def test_fromkeys_rejects_unhashable_keys(self: Self) -> None:
53
+ with self.assertRaises(TypeError):
54
+ FrozenDictSlot.fromkeys([["unhashable"]], 1) # type: ignore
55
+
56
+
57
+ class TestGetMethod(unittest.TestCase):
58
+ def test_get_returns_existing_value(self: Self) -> None:
59
+ mapping: Any = FrozenDictSlot({"a": 1, "b": 2})
60
+
61
+ self.assertEqual(mapping.get("a"), 1)
62
+
63
+ def test_get_returns_none_for_missing_key(self: Self) -> None:
64
+ mapping: Any = FrozenDictSlot({"a": 1})
65
+
66
+ self.assertIsNone(mapping.get("missing"))
67
+
68
+ def test_get_returns_default_for_missing_key(self: Self) -> None:
69
+ mapping: Any = FrozenDictSlot({"a": 1})
70
+
71
+ self.assertEqual(mapping.get("missing", 20), 20)
72
+
73
+ def test_get_returns_existing_value_instead_of_default(self: Self) -> None:
74
+ mapping: Any = FrozenDictSlot({"a": 1})
75
+
76
+ self.assertEqual(mapping.get("a", 20), 1)
77
+
78
+ def test_get_returns_exact_default_object(self: Self) -> None:
79
+ default: Any = []
80
+ mapping: Any = FrozenDictSlot()
81
+
82
+ self.assertIs(mapping.get("missing", default), default)
83
+
84
+ def test_get_accepts_different_hashable_key_types(self: Self) -> None:
85
+ mapping: FrozenDictSlot[abc.Hashable, str]
86
+ mapping = FrozenDictSlot(
87
+ { # type: ignore
88
+ 1: "integer",
89
+ (2, 3): "tuple",
90
+ None: "none",
91
+ }
92
+ )
93
+
94
+ self.assertEqual(mapping.get(1), "integer")
95
+ self.assertEqual(mapping.get((2, 3)), "tuple")
96
+ self.assertEqual(mapping.get(None), "none")
97
+
98
+ def test_get_rejects_unhashable_key(self: Self) -> None:
99
+ self.assertIsNone(FrozenDictSlot().get([]))
100
+
101
+ def test_get_rejects_too_many_arguments(self: Self) -> None:
102
+ with self.assertRaises(TypeError):
103
+ FrozenDictSlot().get("a", 1, 2) # type: ignore[call-overload]
104
+
105
+
106
+ class TestItemsMethod(unittest.TestCase):
107
+ def test_items_contains_all_key_value_pairs(self: Self) -> None:
108
+ mapping: Any = FrozenDictSlot({"a": 1, "b": 2})
109
+
110
+ self.assertEqual(
111
+ set(mapping.items()),
112
+ {("a", 1), ("b", 2)},
113
+ )
114
+
115
+ def test_items_of_empty_mapping_is_empty(self: Self) -> None:
116
+ self.assertEqual(list(FrozenDictSlot().items()), [])
117
+
118
+ def test_items_preserves_insertion_order(self: Self) -> None:
119
+ mapping: Any = FrozenDictSlot(
120
+ [
121
+ ("third", 3),
122
+ ("first", 1),
123
+ ("second", 2),
124
+ ]
125
+ )
126
+
127
+ self.assertEqual(
128
+ list(mapping.items()),
129
+ [("third", 3), ("first", 1), ("second", 2)],
130
+ )
131
+
132
+ def test_items_supports_membership_testing(self: Self) -> None:
133
+ items: Any = FrozenDictSlot({"a": 1, "b": 2}).items()
134
+
135
+ self.assertIn(("a", 1), items)
136
+ self.assertNotIn(("a", 2), items)
137
+
138
+ def test_items_has_correct_length(self: Self) -> None:
139
+ items: Any = FrozenDictSlot(
140
+ {
141
+ "a": 1,
142
+ "b": 2,
143
+ "c": 3,
144
+ }
145
+ ).items()
146
+
147
+ self.assertEqual(len(items), 3)
148
+
149
+ def test_items_can_be_iterated_multiple_times(self: Self) -> None:
150
+ items: Any = FrozenDictSlot({"a": 1, "b": 2}).items()
151
+
152
+ self.assertEqual(list(items), [("a", 1), ("b", 2)])
153
+ self.assertEqual(list(items), [("a", 1), ("b", 2)])
154
+
155
+ def test_items_rejects_arguments(self: Self) -> None:
156
+ with self.assertRaises(TypeError):
157
+ FrozenDictSlot({"a": 1}).items("unexpected") # type: ignore[call-arg]
158
+
159
+
160
+ class TestKeysMethod(unittest.TestCase):
161
+ def test_keys_contains_all_keys(self: Self) -> None:
162
+ mapping: Any = FrozenDictSlot({"a": 1, "b": 2, "c": 3})
163
+
164
+ self.assertEqual(set(mapping.keys()), {"a", "b", "c"})
165
+
166
+ def test_keys_of_empty_mapping_is_empty(self: Self) -> None:
167
+ self.assertEqual(list(FrozenDictSlot().keys()), [])
168
+
169
+ def test_keys_preserves_insertion_order(self: Self) -> None:
170
+ mapping: Any = FrozenDictSlot(
171
+ [
172
+ ("third", 3),
173
+ ("first", 1),
174
+ ("second", 2),
175
+ ]
176
+ )
177
+
178
+ self.assertEqual(
179
+ list(mapping.keys()),
180
+ ["third", "first", "second"],
181
+ )
182
+
183
+ def test_keys_supports_membership_testing(self: Self) -> None:
184
+ keys: Any = FrozenDictSlot({"a": 1, "b": 2}).keys()
185
+
186
+ self.assertIn("a", keys)
187
+ self.assertNotIn("missing", keys)
188
+
189
+ def test_keys_has_correct_length(self: Self) -> None:
190
+ keys: Any = FrozenDictSlot(
191
+ {
192
+ "a": 1,
193
+ "b": 2,
194
+ "c": 3,
195
+ }
196
+ ).keys()
197
+
198
+ self.assertEqual(len(keys), 3)
199
+
200
+ def test_keys_supports_set_operations(self: Self) -> None:
201
+ keys: Any = FrozenDictSlot({"a": 1, "b": 2}).keys()
202
+
203
+ self.assertEqual(keys & {"b", "c"}, {"b"})
204
+ self.assertEqual(keys | {"c"}, {"a", "b", "c"})
205
+
206
+ def test_keys_rejects_arguments(self: Self) -> None:
207
+ with self.assertRaises(TypeError):
208
+ FrozenDictSlot({"a": 1}).keys("unexpected") # type: ignore[call-arg]
209
+
210
+
211
+ class TestValuesMethod(unittest.TestCase):
212
+ def test_values_contains_all_values(self: Self) -> None:
213
+ mapping: Any = FrozenDictSlot({"a": 1, "b": 2, "c": 3})
214
+
215
+ self.assertEqual(list(mapping.values()), [1, 2, 3])
216
+
217
+ def test_values_of_empty_mapping_is_empty(self: Self) -> None:
218
+ self.assertEqual(list(FrozenDictSlot().values()), [])
219
+
220
+ def test_values_preserves_insertion_order(self: Self) -> None:
221
+ mapping: Any = FrozenDictSlot(
222
+ [
223
+ ("third", 3),
224
+ ("first", 1),
225
+ ("second", 2),
226
+ ]
227
+ )
228
+
229
+ self.assertEqual(list(mapping.values()), [3, 1, 2])
230
+
231
+ def test_values_preserves_duplicates(self: Self) -> None:
232
+ mapping: Any = FrozenDictSlot(
233
+ {
234
+ "a": 1,
235
+ "b": 1,
236
+ "c": 2,
237
+ }
238
+ )
239
+
240
+ self.assertEqual(list(mapping.values()), [1, 1, 2])
241
+
242
+ def test_values_supports_membership_testing(self: Self) -> None:
243
+ values: Any = FrozenDictSlot({"a": 1, "b": 2}).values()
244
+
245
+ self.assertIn(1, values)
246
+ self.assertNotIn(3, values)
247
+
248
+ def test_values_has_correct_length(self: Self) -> None:
249
+ values: Any = FrozenDictSlot(
250
+ {
251
+ "a": 1,
252
+ "b": 2,
253
+ "c": 3,
254
+ }
255
+ ).values()
256
+
257
+ self.assertEqual(len(values), 3)
258
+
259
+ def test_values_can_be_iterated_multiple_times(self: Self) -> None:
260
+ values: Any = FrozenDictSlot({"a": 1, "b": 2}).values()
261
+
262
+ self.assertEqual(list(values), [1, 2])
263
+ self.assertEqual(list(values), [1, 2])
264
+
265
+ def test_values_rejects_arguments(self: Self) -> None:
266
+ with self.assertRaises(TypeError):
267
+ FrozenDictSlot({"a": 1}).values("unexpected") # type: ignore[call-arg]
268
+
269
+
270
+ if __name__ == "__main__":
271
+ unittest.main()
@@ -229,5 +229,248 @@ class TestNonMembershipOperator(unittest.TestCase):
229
229
  self.assertFalse({1, 2} not in FrozenSetSlot({frozenset({1, 2})}))
230
230
 
231
231
 
232
+ class TestDifferenceMethod(unittest.TestCase):
233
+ def test_difference_with_overlapping_values(self: Self) -> None:
234
+ result = FrozenSetSlot({1, 2, 3}).difference({2, 3, 4})
235
+
236
+ self.assertEqual(result, frozenset({1}))
237
+
238
+ def test_difference_with_disjoint_values(self: Self) -> None:
239
+ result = FrozenSetSlot({1, 2}).difference({3, 4})
240
+
241
+ self.assertEqual(result, frozenset({1, 2}))
242
+
243
+ def test_difference_with_multiple_iterables(self: Self) -> None:
244
+ result = FrozenSetSlot({1, 2, 3, 4, 5}).difference(
245
+ {2, 3},
246
+ [4],
247
+ (5, 6),
248
+ )
249
+
250
+ self.assertEqual(result, frozenset({1}))
251
+
252
+ def test_difference_without_arguments(self: Self) -> None:
253
+ result = FrozenSetSlot({1, 2}).difference()
254
+
255
+ self.assertEqual(result, frozenset({1, 2}))
256
+
257
+ def test_difference_returns_frozen_set_slot(self: Self) -> None:
258
+ result = FrozenSetSlot({1, 2, 3}).difference({3})
259
+
260
+ self.assertIsInstance(result, FrozenSetSlot)
261
+
262
+ def test_difference_rejects_non_iterable(self: Self) -> None:
263
+ with self.assertRaises(TypeError):
264
+ FrozenSetSlot({1, 2}).difference(3) # type: ignore[arg-type]
265
+
266
+
267
+ class TestIntersectionMethod(unittest.TestCase):
268
+ def test_intersection_with_overlapping_values(self: Self) -> None:
269
+ result = FrozenSetSlot({1, 2, 3}).intersection({2, 3, 4})
270
+
271
+ self.assertEqual(result, frozenset({2, 3}))
272
+
273
+ def test_intersection_with_disjoint_values(self: Self) -> None:
274
+ result = FrozenSetSlot({1, 2}).intersection({3, 4})
275
+
276
+ self.assertEqual(result, frozenset())
277
+
278
+ def test_intersection_with_multiple_iterables(self: Self) -> None:
279
+ result = FrozenSetSlot({1, 2, 3, 4}).intersection(
280
+ [2, 3, 4, 5],
281
+ (3, 4, 6),
282
+ )
283
+
284
+ self.assertEqual(result, frozenset({3, 4}))
285
+
286
+ def test_intersection_without_arguments(self: Self) -> None:
287
+ result = FrozenSetSlot({1, 2}).intersection()
288
+
289
+ self.assertEqual(result, frozenset({1, 2}))
290
+
291
+ def test_intersection_returns_frozen_set_slot(self: Self) -> None:
292
+ result = FrozenSetSlot({1, 2, 3}).intersection({2, 3})
293
+
294
+ self.assertIsInstance(result, FrozenSetSlot)
295
+
296
+ def test_intersection_accepts_generator(self: Self) -> None:
297
+ values = (value for value in range(2, 5))
298
+ result = FrozenSetSlot({1, 2, 3}).intersection(values)
299
+
300
+ self.assertEqual(result, frozenset({2, 3}))
301
+
302
+
303
+ class TestIsDisjointMethod(unittest.TestCase):
304
+ def test_disjoint_iterables(self: Self) -> None:
305
+ result = FrozenSetSlot({1, 2}).isdisjoint({3, 4})
306
+
307
+ self.assertTrue(result)
308
+
309
+ def test_overlapping_iterables(self: Self) -> None:
310
+ result = FrozenSetSlot({1, 2, 3}).isdisjoint({3, 4})
311
+
312
+ self.assertFalse(result)
313
+
314
+ def test_empty_instance_is_disjoint(self: Self) -> None:
315
+ result = FrozenSetSlot().isdisjoint({1, 2})
316
+
317
+ self.assertTrue(result)
318
+
319
+ def test_is_disjoint_with_empty_iterable(self: Self) -> None:
320
+ result = FrozenSetSlot({1, 2}).isdisjoint([])
321
+
322
+ self.assertTrue(result)
323
+
324
+ def test_is_disjoint_accepts_generator(self: Self) -> None:
325
+ values = (value for value in range(3, 6))
326
+ result = FrozenSetSlot({1, 2, 3}).isdisjoint(values)
327
+
328
+ self.assertFalse(result)
329
+
330
+ def test_is_disjoint_rejects_non_iterable(self: Self) -> None:
331
+ with self.assertRaises(TypeError):
332
+ FrozenSetSlot({1, 2}).isdisjoint(3) # type: ignore[arg-type]
333
+
334
+
335
+ class TestIsSubsetMethod(unittest.TestCase):
336
+ def test_proper_subset(self: Self) -> None:
337
+ result = FrozenSetSlot({1, 2}).issubset({1, 2, 3})
338
+
339
+ self.assertTrue(result)
340
+
341
+ def test_equal_set_is_subset(self: Self) -> None:
342
+ result = FrozenSetSlot({1, 2}).issubset([2, 1])
343
+
344
+ self.assertTrue(result)
345
+
346
+ def test_non_subset(self: Self) -> None:
347
+ result = FrozenSetSlot({1, 2, 4}).issubset({1, 2, 3})
348
+
349
+ self.assertFalse(result)
350
+
351
+ def test_empty_instance_is_subset(self: Self) -> None:
352
+ result = FrozenSetSlot().issubset({1, 2})
353
+
354
+ self.assertTrue(result)
355
+
356
+ def test_nonempty_instance_is_not_subset_of_empty_iterable(
357
+ self: Self,
358
+ ) -> None:
359
+ result = FrozenSetSlot({1}).issubset([])
360
+
361
+ self.assertFalse(result)
362
+
363
+ def test_is_subset_accepts_generator(self: Self) -> None:
364
+ values = (value for value in range(1, 4))
365
+ result = FrozenSetSlot({1, 2}).issubset(values)
366
+
367
+ self.assertTrue(result)
368
+
369
+
370
+ class TestIsSupersetMethod(unittest.TestCase):
371
+ def test_proper_superset(self: Self) -> None:
372
+ result = FrozenSetSlot({1, 2, 3}).issuperset({1, 2})
373
+
374
+ self.assertTrue(result)
375
+
376
+ def test_equal_set_is_superset(self: Self) -> None:
377
+ result = FrozenSetSlot({1, 2}).issuperset([2, 1])
378
+
379
+ self.assertTrue(result)
380
+
381
+ def test_non_superset(self: Self) -> None:
382
+ result = FrozenSetSlot({1, 2}).issuperset({1, 2, 3})
383
+
384
+ self.assertFalse(result)
385
+
386
+ def test_every_instance_is_superset_of_empty_iterable(self: Self) -> None:
387
+ result = FrozenSetSlot({1, 2}).issuperset([])
388
+
389
+ self.assertTrue(result)
390
+
391
+ def test_empty_instance_is_not_superset_of_nonempty_iterable(
392
+ self: Self,
393
+ ) -> None:
394
+ result = FrozenSetSlot().issuperset({1})
395
+
396
+ self.assertFalse(result)
397
+
398
+ def test_is_superset_accepts_generator(self: Self) -> None:
399
+ values = (value for value in range(1, 3))
400
+ result = FrozenSetSlot({1, 2, 3}).issuperset(values)
401
+
402
+ self.assertTrue(result)
403
+
404
+
405
+ class TestSymmetricDifferenceMethod(unittest.TestCase):
406
+ def test_symmetric_difference_with_overlap(self: Self) -> None:
407
+ result = FrozenSetSlot({1, 2, 3}).symmetric_difference({3, 4})
408
+
409
+ self.assertEqual(result, frozenset({1, 2, 4}))
410
+
411
+ def test_symmetric_difference_with_disjoint_values(self: Self) -> None:
412
+ result = FrozenSetSlot({1, 2}).symmetric_difference([3, 4])
413
+
414
+ self.assertEqual(result, frozenset({1, 2, 3, 4}))
415
+
416
+ def test_symmetric_difference_with_equal_values(self: Self) -> None:
417
+ result = FrozenSetSlot({1, 2}).symmetric_difference((2, 1))
418
+
419
+ self.assertEqual(result, frozenset())
420
+
421
+ def test_symmetric_difference_with_empty_iterable(self: Self) -> None:
422
+ result = FrozenSetSlot({1, 2}).symmetric_difference([])
423
+
424
+ self.assertEqual(result, frozenset({1, 2}))
425
+
426
+ def test_symmetric_difference_returns_frozen_set_slot(self: Self) -> None:
427
+ result = FrozenSetSlot({1, 2}).symmetric_difference({2, 3})
428
+
429
+ self.assertIsInstance(result, FrozenSetSlot)
430
+
431
+ def test_symmetric_difference_rejects_multiple_arguments(
432
+ self: Self,
433
+ ) -> None:
434
+ with self.assertRaises(TypeError):
435
+ FrozenSetSlot({1, 2}).symmetric_difference({2}, {3}) # type: ignore[call-arg]
436
+
437
+
438
+ class TestUnionMethod(unittest.TestCase):
439
+ def test_union_with_overlapping_values(self: Self) -> None:
440
+ result = FrozenSetSlot({1, 2, 3}).union({3, 4})
441
+
442
+ self.assertEqual(result, frozenset({1, 2, 3, 4}))
443
+
444
+ def test_union_with_disjoint_values(self: Self) -> None:
445
+ result = FrozenSetSlot({1, 2}).union([3, 4])
446
+
447
+ self.assertEqual(result, frozenset({1, 2, 3, 4}))
448
+
449
+ def test_union_with_multiple_iterables(self: Self) -> None:
450
+ result = FrozenSetSlot({1}).union(
451
+ [2, 3],
452
+ (3, 4),
453
+ frozenset({5}),
454
+ )
455
+
456
+ self.assertEqual(result, frozenset({1, 2, 3, 4, 5}))
457
+
458
+ def test_union_without_arguments(self: Self) -> None:
459
+ result = FrozenSetSlot({1, 2}).union()
460
+
461
+ self.assertEqual(result, frozenset({1, 2}))
462
+
463
+ def test_union_returns_frozen_set_slot(self: Self) -> None:
464
+ result = FrozenSetSlot({1, 2}).union({3})
465
+
466
+ self.assertIsInstance(result, FrozenSetSlot)
467
+
468
+ def test_union_accepts_generator(self: Self) -> None:
469
+ values = (value for value in range(2, 5))
470
+ result = FrozenSetSlot({1, 2}).union(values)
471
+
472
+ self.assertEqual(result, frozenset({1, 2, 3, 4}))
473
+
474
+
232
475
  if __name__ == "__main__":
233
476
  unittest.main()
@@ -136,8 +136,7 @@ class TestGet(unittest.TestCase):
136
136
  def test_get_rejects_unhashable_key(self: Self) -> None:
137
137
  value: MutableDictSlot[Any, Any]
138
138
  value = MutableDictSlot({"a": 1})
139
- with self.assertRaises(TypeError):
140
- value.get([])
139
+ self.assertIsNone(value.get([]))
141
140
  self.assertEqual(value, {"a": 1})
142
141
 
143
142
 
@@ -141,6 +141,10 @@ args = [
141
141
  [datatypes.FrozenListSlot.varia]
142
142
  generic = 1
143
143
 
144
+ [datatypes.FrozenObjectLike.issubclass]
145
+ "collections.abc.Hashable" = true
146
+ "datahold.Object" = true
147
+
144
148
  [datatypes.FrozenObjectSlot.issubclass]
145
149
  "datahold.ObjectSlot" = true
146
150
 
File without changes