datahold 4.0.0.dev28__tar.gz → 4.0.0.dev30__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.dev28 → datahold-4.0.0.dev30}/PKG-INFO +1 -1
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/pyproject.toml +1 -1
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold/__init__.py +101 -75
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold.egg-info/SOURCES.txt +1 -0
- datahold-4.0.0.dev30/tests/test_FrozenDictSlot.py +271 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_FrozenSetSlot.py +243 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_MutableDictSlot.py +1 -2
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/testdata.toml +4 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/README.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/run_tests.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/setup.cfg +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/src/datahold.egg-info/top_level.txt +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_FrozenListSlot.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_MutableListSlot.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_MutableSetSlot.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_abstractmethods.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_all.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_attributes.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_docs.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_generic.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_issubclass.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_metaclass.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_name.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_nested.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_sorted.py +0 -0
- {datahold-4.0.0.dev28 → datahold-4.0.0.dev30}/tests/test_variance.py +0 -0
|
@@ -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",
|
|
@@ -36,15 +37,7 @@ from abc import ABCMeta, abstractmethod
|
|
|
36
37
|
from collections import abc
|
|
37
38
|
from contextlib import contextmanager
|
|
38
39
|
from types import NotImplementedType, TracebackType
|
|
39
|
-
from typing import
|
|
40
|
-
Any,
|
|
41
|
-
Never,
|
|
42
|
-
Optional,
|
|
43
|
-
Protocol,
|
|
44
|
-
Self,
|
|
45
|
-
SupportsIndex,
|
|
46
|
-
overload,
|
|
47
|
-
)
|
|
40
|
+
from typing import Any, Never, Protocol, Self, SupportsIndex, overload
|
|
48
41
|
|
|
49
42
|
import setdoc
|
|
50
43
|
from frozendict import frozendict
|
|
@@ -86,7 +79,7 @@ class Collection_Frozen[Item](
|
|
|
86
79
|
def __contains__(self: Self, other: Never, /) -> bool: ...
|
|
87
80
|
|
|
88
81
|
|
|
89
|
-
class
|
|
82
|
+
class SupportsAddAndDiscard[Item: abc.Hashable](Protocol):
|
|
90
83
|
@setdoc.basic
|
|
91
84
|
def add(self: Self, item: Item, /) -> object: ...
|
|
92
85
|
@setdoc.basic
|
|
@@ -95,15 +88,15 @@ class MutableSet_Mutable[Item: abc.Hashable](Protocol):
|
|
|
95
88
|
|
|
96
89
|
class Mapping_Frozen[Key, Value](Collection_Frozen[Key], Protocol):
|
|
97
90
|
@setdoc.basic
|
|
98
|
-
def __getitem__(self: Self, key:
|
|
99
|
-
class
|
|
91
|
+
def __getitem__(self: Self, key: Never, /) -> Value: ...
|
|
92
|
+
class SupportsDelitemAndSetitem[Key, Value](
|
|
100
93
|
Protocol,
|
|
101
94
|
):
|
|
102
95
|
@setdoc.basic
|
|
103
|
-
def __delitem__(self: Self, key:
|
|
96
|
+
def __delitem__(self: Self, key: Key | str, /) -> object: ...
|
|
104
97
|
@setdoc.basic
|
|
105
98
|
def __setitem__(
|
|
106
|
-
self: Self, key:
|
|
99
|
+
self: Self, key: Key | str, value: Value | None, /
|
|
107
100
|
) -> object: ...
|
|
108
101
|
class Sequence_Frozen[Item](
|
|
109
102
|
Collection_Frozen[Item],
|
|
@@ -169,16 +162,16 @@ class MutableSequence_Mutable[Item](Protocol):
|
|
|
169
162
|
|
|
170
163
|
### TYPE ALIASES ###
|
|
171
164
|
|
|
172
|
-
type Dict[Key, Value] = dict[Key | str,
|
|
165
|
+
type Dict[Key, Value] = dict[Key | str, Value | None]
|
|
173
166
|
|
|
174
|
-
type
|
|
175
|
-
SupportsKeysAndGetitem[Key | str,
|
|
176
|
-
| abc.Iterable[tuple[Key | str,
|
|
167
|
+
type Dict_Init[Key, Value] = (
|
|
168
|
+
SupportsKeysAndGetitem[Key | str, Value | None]
|
|
169
|
+
| abc.Iterable[tuple[Key | str, Value | None]]
|
|
177
170
|
)
|
|
178
171
|
|
|
179
|
-
type FrozenDict[Key, Value] = frozendict[Key | str,
|
|
172
|
+
type FrozenDict[Key, Value] = frozendict[Key | str, Value | None]
|
|
180
173
|
|
|
181
|
-
type Slice[Index] = slice[
|
|
174
|
+
type Slice[Index] = slice[Index | None, Index | None, Index | None]
|
|
182
175
|
|
|
183
176
|
|
|
184
177
|
### OBJECT ###
|
|
@@ -205,6 +198,19 @@ class MutableObject[Frozen, Mutable](Object[Frozen]):
|
|
|
205
198
|
### OBJECT-LIKE ###
|
|
206
199
|
|
|
207
200
|
|
|
201
|
+
class FrozenObjectLike[Frozen: abc.Hashable](
|
|
202
|
+
Object[Frozen],
|
|
203
|
+
abc.Hashable,
|
|
204
|
+
):
|
|
205
|
+
"""Provide abc for custom frozen object-like."""
|
|
206
|
+
|
|
207
|
+
__slots__ = ()
|
|
208
|
+
|
|
209
|
+
@setdoc.basic
|
|
210
|
+
def __hash__(self: Self, /) -> int:
|
|
211
|
+
return hash(self.__frozen__())
|
|
212
|
+
|
|
213
|
+
|
|
208
214
|
class MutableObjectLike[Frozen, Mutable](
|
|
209
215
|
MutableObject[Frozen, Mutable],
|
|
210
216
|
):
|
|
@@ -232,7 +238,8 @@ class ObjectSlot[Frozen](
|
|
|
232
238
|
_slot: Frozen
|
|
233
239
|
|
|
234
240
|
|
|
235
|
-
class FrozenObjectSlot[Frozen](
|
|
241
|
+
class FrozenObjectSlot[Frozen: abc.Hashable](
|
|
242
|
+
FrozenObjectLike[Frozen],
|
|
236
243
|
ObjectSlot[Frozen],
|
|
237
244
|
):
|
|
238
245
|
"""Provide slotted abc for custom frozen object-like."""
|
|
@@ -284,7 +291,7 @@ class Set[Item: abc.Hashable](Collection[Item], abc.Set[Item]):
|
|
|
284
291
|
class MutableSet[Item: abc.Hashable](
|
|
285
292
|
Set[Item],
|
|
286
293
|
abc.MutableSet[Item],
|
|
287
|
-
MutableObject[Collection_Frozen[Item],
|
|
294
|
+
MutableObject[Collection_Frozen[Item], SupportsAddAndDiscard[Item]],
|
|
288
295
|
):
|
|
289
296
|
"""Provide abc for custom mutable set."""
|
|
290
297
|
|
|
@@ -314,7 +321,7 @@ class SetLike[Item: abc.Hashable](Set[Item]):
|
|
|
314
321
|
def __frozen__(self: Self, /) -> frozenset[Item]: ...
|
|
315
322
|
@abstractmethod
|
|
316
323
|
@setdoc.basic
|
|
317
|
-
def __init__(self: Self,
|
|
324
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None: ...
|
|
318
325
|
@setdoc.basic
|
|
319
326
|
def difference(self: Self, /, *others: abc.Iterable[abc.Hashable]) -> Self:
|
|
320
327
|
return type(self)(self.__frozen__().difference(*others))
|
|
@@ -342,15 +349,14 @@ class SetLike[Item: abc.Hashable](Set[Item]):
|
|
|
342
349
|
return type(self)(self.__frozen__().union(*others))
|
|
343
350
|
|
|
344
351
|
|
|
345
|
-
class FrozenSetLike[Item: abc.Hashable](
|
|
352
|
+
class FrozenSetLike[Item: abc.Hashable](
|
|
353
|
+
FrozenObjectLike[frozenset[Item]],
|
|
354
|
+
SetLike[Item],
|
|
355
|
+
):
|
|
346
356
|
"""Provide abc for custom frozen set-like."""
|
|
347
357
|
|
|
348
358
|
__slots__ = ()
|
|
349
359
|
|
|
350
|
-
@setdoc.basic
|
|
351
|
-
def __hash__(self: Self, /) -> int:
|
|
352
|
-
return hash(self.__frozen__())
|
|
353
|
-
|
|
354
360
|
|
|
355
361
|
class MutableSetLike[Item: abc.Hashable](
|
|
356
362
|
SetLike[Item],
|
|
@@ -367,9 +373,9 @@ class MutableSetLike[Item: abc.Hashable](
|
|
|
367
373
|
return frozenset(mutable)
|
|
368
374
|
|
|
369
375
|
@setdoc.basic
|
|
370
|
-
def __init__(self: Self,
|
|
376
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
|
|
371
377
|
with self.__mutate__() as mutable:
|
|
372
|
-
mutable.update(
|
|
378
|
+
mutable.update(other)
|
|
373
379
|
|
|
374
380
|
@setdoc.basic
|
|
375
381
|
def difference_update(
|
|
@@ -410,8 +416,8 @@ class FrozenSetSlot[Item](
|
|
|
410
416
|
__slots__ = ()
|
|
411
417
|
|
|
412
418
|
@setdoc.basic
|
|
413
|
-
def __init__(self: Self,
|
|
414
|
-
self._slot = frozenset(
|
|
419
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
|
|
420
|
+
self._slot = frozenset(other)
|
|
415
421
|
|
|
416
422
|
|
|
417
423
|
class MutableSetSlot[Item](
|
|
@@ -428,10 +434,10 @@ class MutableSetSlot[Item](
|
|
|
428
434
|
self: Self,
|
|
429
435
|
/,
|
|
430
436
|
) -> abc.Generator[set[Item], None, None]:
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
yield
|
|
434
|
-
self._slot = frozenset(
|
|
437
|
+
mutable: set[Item]
|
|
438
|
+
mutable = set(getattr(self, "_slot", ()))
|
|
439
|
+
yield mutable
|
|
440
|
+
self._slot = frozenset(mutable)
|
|
435
441
|
|
|
436
442
|
|
|
437
443
|
### MAPPING ###
|
|
@@ -449,14 +455,35 @@ class Mapping[Key: abc.Hashable, Value](
|
|
|
449
455
|
@setdoc.basic
|
|
450
456
|
def __frozen__(self: Self, /) -> Mapping_Frozen[Key, Value]: ...
|
|
451
457
|
@setdoc.basic
|
|
452
|
-
def __getitem__(self: Self, key:
|
|
453
|
-
|
|
458
|
+
def __getitem__(self: Self, key: object, /) -> Value:
|
|
459
|
+
try:
|
|
460
|
+
return self.__frozen__()[key] # type: ignore[index]
|
|
461
|
+
except TypeError:
|
|
462
|
+
raise KeyError(key) from None
|
|
463
|
+
|
|
464
|
+
@overload
|
|
465
|
+
def get(self, key: object) -> Value | None: ...
|
|
466
|
+
@overload
|
|
467
|
+
def get[Value_](self, key: object, default: Value_) -> Value | Value_: ...
|
|
468
|
+
@setdoc.basic
|
|
469
|
+
def get[Value_](
|
|
470
|
+
self: Self, key: object, default: Value_ | None = None
|
|
471
|
+
) -> Value | Value_ | None:
|
|
472
|
+
# this method is just as Mapping.get
|
|
473
|
+
# with the signature corrected
|
|
474
|
+
try:
|
|
475
|
+
return self[key]
|
|
476
|
+
except KeyError:
|
|
477
|
+
return default
|
|
454
478
|
|
|
455
479
|
|
|
456
480
|
class MutableMapping[Key: abc.Hashable, Value](
|
|
457
481
|
Mapping[Key, Value],
|
|
458
|
-
abc.MutableMapping[Key | str,
|
|
459
|
-
MutableObject[
|
|
482
|
+
abc.MutableMapping[Key | str, Value | None],
|
|
483
|
+
MutableObject[
|
|
484
|
+
Mapping_Frozen[Key | str, Value | None],
|
|
485
|
+
SupportsDelitemAndSetitem[Key | str, Value | None],
|
|
486
|
+
],
|
|
460
487
|
):
|
|
461
488
|
"""Provide abc for custom mutable mapping."""
|
|
462
489
|
|
|
@@ -469,7 +496,7 @@ class MutableMapping[Key: abc.Hashable, Value](
|
|
|
469
496
|
|
|
470
497
|
@setdoc.basic
|
|
471
498
|
def __setitem__(
|
|
472
|
-
self: Self, key: Key | str, value:
|
|
499
|
+
self: Self, key: Key | str, value: Value | None, /
|
|
473
500
|
) -> None:
|
|
474
501
|
with self.__mutate__() as mutable:
|
|
475
502
|
mutable[key] = value
|
|
@@ -479,7 +506,7 @@ class MutableMapping[Key: abc.Hashable, Value](
|
|
|
479
506
|
|
|
480
507
|
|
|
481
508
|
class DictLike[Key: abc.Hashable, Value](
|
|
482
|
-
Mapping[Key | str,
|
|
509
|
+
Mapping[Key | str, Value | None],
|
|
483
510
|
):
|
|
484
511
|
"""Provide abc for custom dict-like."""
|
|
485
512
|
|
|
@@ -487,21 +514,20 @@ class DictLike[Key: abc.Hashable, Value](
|
|
|
487
514
|
|
|
488
515
|
@abstractmethod
|
|
489
516
|
@setdoc.basic
|
|
490
|
-
def __frozen__(self: Self, /) -> FrozenDict[Key, Value]:
|
|
491
|
-
...
|
|
517
|
+
def __frozen__(self: Self, /) -> FrozenDict[Key, Value]: ...
|
|
492
518
|
|
|
493
519
|
@abstractmethod
|
|
494
520
|
@setdoc.basic
|
|
495
521
|
def __init__(
|
|
496
522
|
self: Self,
|
|
497
|
-
|
|
523
|
+
other: Dict_Init[Key, Value] = (),
|
|
498
524
|
/,
|
|
499
525
|
**kwargs: Value,
|
|
500
526
|
): ...
|
|
501
527
|
@setdoc.basic
|
|
502
528
|
def __or__[Key_, Value_](
|
|
503
529
|
self: Self,
|
|
504
|
-
other:
|
|
530
|
+
other: Dict_Init[Key_, Value_],
|
|
505
531
|
/,
|
|
506
532
|
) -> DictLike[Key | Key_, Value | Value_]:
|
|
507
533
|
data: FrozenDict[Key, Value]
|
|
@@ -518,24 +544,20 @@ class DictLike[Key: abc.Hashable, Value](
|
|
|
518
544
|
def fromkeys(
|
|
519
545
|
cls: type[Self],
|
|
520
546
|
iterable: abc.Iterable[Key | str],
|
|
521
|
-
value:
|
|
547
|
+
value: Value | None = None,
|
|
522
548
|
/,
|
|
523
549
|
) -> Self:
|
|
524
550
|
return cls(dict.fromkeys(iterable, value))
|
|
525
551
|
|
|
526
552
|
|
|
527
553
|
class FrozenDictLike[Key: abc.Hashable, Value](
|
|
554
|
+
FrozenObjectLike[FrozenDict[Key, Value]],
|
|
528
555
|
DictLike[Key, Value],
|
|
529
|
-
abc.Hashable,
|
|
530
556
|
):
|
|
531
557
|
"""Provide abc for custom frozen dict-like."""
|
|
532
558
|
|
|
533
559
|
__slots__ = ()
|
|
534
560
|
|
|
535
|
-
@setdoc.basic
|
|
536
|
-
def __hash__(self: Self, /) -> int:
|
|
537
|
-
return hash(self.__frozen__())
|
|
538
|
-
|
|
539
561
|
|
|
540
562
|
class MutableDictLike[Key: abc.Hashable, Value](
|
|
541
563
|
DictLike[Key, Value],
|
|
@@ -547,7 +569,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
547
569
|
__slots__ = ()
|
|
548
570
|
|
|
549
571
|
@setdoc.basic
|
|
550
|
-
def __frozen__(
|
|
572
|
+
def __frozen__(
|
|
551
573
|
self: Self,
|
|
552
574
|
/,
|
|
553
575
|
) -> FrozenDict[Key, Value]:
|
|
@@ -557,16 +579,16 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
557
579
|
@setdoc.basic
|
|
558
580
|
def __init__(
|
|
559
581
|
self: Self,
|
|
560
|
-
|
|
582
|
+
other: Dict_Init[Key, Value] = (),
|
|
561
583
|
/,
|
|
562
|
-
**kwargs:
|
|
584
|
+
**kwargs: Value | None,
|
|
563
585
|
):
|
|
564
|
-
self.update(
|
|
586
|
+
self.update(other, **kwargs)
|
|
565
587
|
|
|
566
588
|
@setdoc.basic
|
|
567
589
|
def __ior__( # type: ignore[override]
|
|
568
590
|
self: Self,
|
|
569
|
-
other:
|
|
591
|
+
other: Dict_Init[Key, Value],
|
|
570
592
|
/,
|
|
571
593
|
) -> Self:
|
|
572
594
|
with self.__mutate__() as mutable:
|
|
@@ -574,7 +596,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
574
596
|
return self
|
|
575
597
|
|
|
576
598
|
@setdoc.basic
|
|
577
|
-
def popitem(self: Self, /) -> tuple[Key | str,
|
|
599
|
+
def popitem(self: Self, /) -> tuple[Key | str, Value | None]:
|
|
578
600
|
# for most dict d
|
|
579
601
|
# dict.popitem(d) and collections.abc.MutableMapping.popitem(d)
|
|
580
602
|
# behave differently
|
|
@@ -585,7 +607,7 @@ class MutableDictLike[Key: abc.Hashable, Value](
|
|
|
585
607
|
### DICT-SLOT ###
|
|
586
608
|
|
|
587
609
|
|
|
588
|
-
class FrozenDictSlot[Key: abc.Hashable, Value](
|
|
610
|
+
class FrozenDictSlot[Key: abc.Hashable, Value](
|
|
589
611
|
FrozenObjectSlot[FrozenDict[Key, Value]],
|
|
590
612
|
FrozenDictLike[Key, Value],
|
|
591
613
|
):
|
|
@@ -596,11 +618,11 @@ class FrozenDictSlot[Key: abc.Hashable, Value]( # type: ignore[misc]
|
|
|
596
618
|
@setdoc.basic
|
|
597
619
|
def __init__(
|
|
598
620
|
self: Self,
|
|
599
|
-
|
|
621
|
+
other: Dict_Init[Key, Value] = (),
|
|
600
622
|
/,
|
|
601
|
-
**kwargs:
|
|
623
|
+
**kwargs: Value | None,
|
|
602
624
|
) -> None:
|
|
603
|
-
self._slot = frozendict(
|
|
625
|
+
self._slot = frozendict(other, **kwargs) # type: ignore[arg-type]
|
|
604
626
|
|
|
605
627
|
|
|
606
628
|
class MutableDictSlot[Key: abc.Hashable, Value](
|
|
@@ -617,10 +639,10 @@ class MutableDictSlot[Key: abc.Hashable, Value](
|
|
|
617
639
|
self: Self,
|
|
618
640
|
/,
|
|
619
641
|
) -> abc.Generator[Dict[Key, Value], None, None]:
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
yield
|
|
623
|
-
self._slot = frozendict(
|
|
642
|
+
mutable: Dict[Key, Value]
|
|
643
|
+
mutable = dict(getattr(self, "_slot", ()))
|
|
644
|
+
yield mutable
|
|
645
|
+
self._slot = frozendict(mutable)
|
|
624
646
|
|
|
625
647
|
|
|
626
648
|
### SEQUENCE ###
|
|
@@ -769,7 +791,7 @@ class ListLike[Item](Sequence[Item]):
|
|
|
769
791
|
|
|
770
792
|
@abstractmethod
|
|
771
793
|
@setdoc.basic
|
|
772
|
-
def __init__(self: Self,
|
|
794
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None: ...
|
|
773
795
|
|
|
774
796
|
@setdoc.basic
|
|
775
797
|
def __le__(self: Self, other: ListLike[Any], /) -> bool:
|
|
@@ -799,7 +821,10 @@ class ListLike[Item](Sequence[Item]):
|
|
|
799
821
|
return f"{type(self).__name__}({list(self)!r})"
|
|
800
822
|
|
|
801
823
|
|
|
802
|
-
class FrozenListLike[Item](
|
|
824
|
+
class FrozenListLike[Item](
|
|
825
|
+
FrozenObjectLike[tuple[Item, ...]],
|
|
826
|
+
ListLike[Item],
|
|
827
|
+
):
|
|
803
828
|
"""Provide abc for custom frozen list-like."""
|
|
804
829
|
|
|
805
830
|
__slots__ = ()
|
|
@@ -843,9 +868,9 @@ class MutableListLike[Item](
|
|
|
843
868
|
return self
|
|
844
869
|
|
|
845
870
|
@setdoc.basic
|
|
846
|
-
def __init__(self: Self,
|
|
871
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
|
|
847
872
|
with self.__mutate__() as mutable:
|
|
848
|
-
mutable.extend(
|
|
873
|
+
mutable.extend(other)
|
|
849
874
|
|
|
850
875
|
@overload
|
|
851
876
|
@setdoc.basic
|
|
@@ -894,8 +919,8 @@ class FrozenListSlot[Item](
|
|
|
894
919
|
__slots__ = ()
|
|
895
920
|
|
|
896
921
|
@setdoc.basic
|
|
897
|
-
def __init__(self: Self,
|
|
898
|
-
self._slot = tuple(
|
|
922
|
+
def __init__(self: Self, other: abc.Iterable[Item] = (), /) -> None:
|
|
923
|
+
self._slot = tuple(other)
|
|
899
924
|
|
|
900
925
|
|
|
901
926
|
class MutableListSlot[Item](
|
|
@@ -909,6 +934,7 @@ class MutableListSlot[Item](
|
|
|
909
934
|
@contextmanager
|
|
910
935
|
@setdoc.basic
|
|
911
936
|
def __mutate__(self: Self, /) -> abc.Generator[list[Item], None, None]:
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
937
|
+
mutable: list[Item]
|
|
938
|
+
mutable = list(getattr(self, "_slot", ()))
|
|
939
|
+
yield mutable
|
|
940
|
+
self._slot = tuple(mutable)
|
|
@@ -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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|