datahold 4.0.0.dev5__tar.gz → 4.0.0.dev7__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.dev5 → datahold-4.0.0.dev7}/PKG-INFO +1 -1
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/pyproject.toml +1 -1
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold/__init__.py +41 -169
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold.egg-info/SOURCES.txt +0 -4
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_FrozenHoldDict.py +56 -2
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_FrozenHoldList.py +55 -2
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_FrozenHoldSet.py +60 -3
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_HoldDict.py +49 -3
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_HoldList.py +48 -2
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_HoldSet.py +48 -1
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/tests/test_testdata.py +54 -0
- datahold-4.0.0.dev7/tests/testdata.toml +136 -0
- datahold-4.0.0.dev5/tests/test_datahold_TestCopy.py +0 -81
- datahold-4.0.0.dev5/tests/test_datahold_TestDataAttribute.py +0 -46
- datahold-4.0.0.dev5/tests/test_datahold_TestFrozenMutability.py +0 -36
- datahold-4.0.0.dev5/tests/test_datahold_TestMutableBehavior.py +0 -33
- datahold-4.0.0.dev5/tests/testdata.toml +0 -209
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/README.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/run_tests.py +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/setup.cfg +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev5 → datahold-4.0.0.dev7}/src/datahold.egg-info/top_level.txt +0 -0
|
@@ -10,19 +10,13 @@ __all__: list[str] = [
|
|
|
10
10
|
"BaseDataMapping",
|
|
11
11
|
"BaseDataSequence",
|
|
12
12
|
"BaseDataSet",
|
|
13
|
-
"
|
|
13
|
+
"BaseHoldObject",
|
|
14
14
|
"DataDict",
|
|
15
15
|
"DataList",
|
|
16
16
|
"DataSet",
|
|
17
17
|
"FrozenDataDict",
|
|
18
18
|
"FrozenDataList",
|
|
19
19
|
"FrozenDataSet",
|
|
20
|
-
"FrozenHoldDict",
|
|
21
|
-
"FrozenHoldList",
|
|
22
|
-
"FrozenHoldSet",
|
|
23
|
-
"HoldDict",
|
|
24
|
-
"HoldList",
|
|
25
|
-
"HoldSet",
|
|
26
20
|
]
|
|
27
21
|
|
|
28
22
|
import enum
|
|
@@ -45,6 +39,11 @@ from frozendict import frozendict
|
|
|
45
39
|
### UTILS ###
|
|
46
40
|
|
|
47
41
|
|
|
42
|
+
class Copyable(Protocol):
|
|
43
|
+
@setdoc.basic
|
|
44
|
+
def copy(self: Self) -> Self: ...
|
|
45
|
+
|
|
46
|
+
|
|
48
47
|
class Missing(enum.Enum):
|
|
49
48
|
missing = None
|
|
50
49
|
|
|
@@ -61,6 +60,23 @@ class SupportsKeysAndGetitem[Key, Value](Protocol):
|
|
|
61
60
|
def keys(self: Self) -> abc.Iterable[Key]: ...
|
|
62
61
|
|
|
63
62
|
|
|
63
|
+
### OBJECT ###
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class BaseHoldObject[HoldData: Copyable]:
|
|
67
|
+
"""Provide abc for usable classes with slots."""
|
|
68
|
+
|
|
69
|
+
__slots__ = ("_data",)
|
|
70
|
+
|
|
71
|
+
@setdoc.basic
|
|
72
|
+
def __fget__(self: Self) -> HoldData:
|
|
73
|
+
return self._data.copy()
|
|
74
|
+
|
|
75
|
+
@setdoc.basic
|
|
76
|
+
def __fset__(self: Self, data: HoldData, /) -> None:
|
|
77
|
+
self._data: HoldData = data
|
|
78
|
+
|
|
79
|
+
|
|
64
80
|
### COLLECTION ###
|
|
65
81
|
|
|
66
82
|
|
|
@@ -103,12 +119,6 @@ class BaseDataCollection[Item](
|
|
|
103
119
|
def __fget__(self: Self) -> Data[Item]: ...
|
|
104
120
|
|
|
105
121
|
|
|
106
|
-
class BaseHoldCollection[Item](BaseDataCollection[Item]):
|
|
107
|
-
"""Provide abc for usable classes with slots."""
|
|
108
|
-
|
|
109
|
-
__slots__ = ("_data",)
|
|
110
|
-
|
|
111
|
-
|
|
112
122
|
### ABSTRACT SET ###
|
|
113
123
|
|
|
114
124
|
|
|
@@ -134,7 +144,7 @@ class BaseDataSet[Item: abc.Hashable](BaseDataAbstractSet[Item]):
|
|
|
134
144
|
|
|
135
145
|
@abstractmethod
|
|
136
146
|
@setdoc.basic
|
|
137
|
-
def __fget__(self: Self) ->
|
|
147
|
+
def __fget__(self: Self) -> Data[Item]: ...
|
|
138
148
|
|
|
139
149
|
@abstractmethod
|
|
140
150
|
@setdoc.basic
|
|
@@ -189,23 +199,6 @@ class FrozenDataSet[Item: abc.Hashable](BaseDataSet[Item], abc.Hashable):
|
|
|
189
199
|
return hash(frozenset(self.__fget__()))
|
|
190
200
|
|
|
191
201
|
|
|
192
|
-
class FrozenHoldSet[Item: abc.Hashable](
|
|
193
|
-
FrozenDataSet[Item],
|
|
194
|
-
BaseHoldCollection[Item],
|
|
195
|
-
):
|
|
196
|
-
"""Provide usable frozen set-like with slots."""
|
|
197
|
-
|
|
198
|
-
__slots__ = ()
|
|
199
|
-
|
|
200
|
-
@setdoc.basic
|
|
201
|
-
def __fget__(self: Self) -> FrozenHoldSet.Data[Item]:
|
|
202
|
-
return set(self._data)
|
|
203
|
-
|
|
204
|
-
@setdoc.basic
|
|
205
|
-
def __fset__(self: Self, data: FrozenHoldSet.Data[Item], /) -> None:
|
|
206
|
-
self._data: FrozenHoldSet.Data[Item] = data
|
|
207
|
-
|
|
208
|
-
|
|
209
202
|
class DataSet[Item: abc.Hashable](
|
|
210
203
|
BaseDataSet[Item],
|
|
211
204
|
abc.MutableSet[Item],
|
|
@@ -214,18 +207,6 @@ class DataSet[Item: abc.Hashable](
|
|
|
214
207
|
|
|
215
208
|
__slots__ = ()
|
|
216
209
|
|
|
217
|
-
@abstractmethod
|
|
218
|
-
@setdoc.basic
|
|
219
|
-
def __fget__(self: Self) -> DataSet.Data[Item]: ...
|
|
220
|
-
|
|
221
|
-
@abstractmethod
|
|
222
|
-
@setdoc.basic
|
|
223
|
-
def __fset__(
|
|
224
|
-
self: Self,
|
|
225
|
-
value: DataSet.Data[Item],
|
|
226
|
-
/,
|
|
227
|
-
) -> None: ...
|
|
228
|
-
|
|
229
210
|
@setdoc.basic
|
|
230
211
|
def add(self: Self, item: Item, /) -> None:
|
|
231
212
|
self.__fset__(self.__fget__() | {item})
|
|
@@ -241,57 +222,40 @@ class DataSet[Item: abc.Hashable](
|
|
|
241
222
|
*others: abc.Iterable[abc.Hashable],
|
|
242
223
|
) -> None:
|
|
243
224
|
data: set[Item]
|
|
244
|
-
data =
|
|
225
|
+
data = self.__fget__()
|
|
245
226
|
data.difference_update(*others)
|
|
246
|
-
self.__fset__(
|
|
227
|
+
self.__fset__(data)
|
|
247
228
|
|
|
248
229
|
@setdoc.basic
|
|
249
230
|
def discard(self: Self, item: abc.Hashable, /) -> None:
|
|
250
231
|
data: set[Item]
|
|
251
|
-
data =
|
|
232
|
+
data = self.__fget__()
|
|
252
233
|
data.discard(item)
|
|
253
|
-
self.__fset__(
|
|
234
|
+
self.__fset__(data)
|
|
254
235
|
|
|
255
236
|
@setdoc.basic
|
|
256
237
|
def intersection_update(
|
|
257
238
|
self: Self, /, *others: abc.Iterable[abc.Hashable]
|
|
258
239
|
) -> None:
|
|
259
240
|
data: set[Item]
|
|
260
|
-
data =
|
|
241
|
+
data = self.__fget__()
|
|
261
242
|
data.intersection_update(*others)
|
|
262
|
-
self.__fset__(
|
|
243
|
+
self.__fset__(data)
|
|
263
244
|
|
|
264
245
|
@setdoc.basic
|
|
265
246
|
def symmetric_difference_update(
|
|
266
247
|
self: Self, other: abc.Iterable[Item], /
|
|
267
248
|
) -> None:
|
|
268
249
|
data: set[Item]
|
|
269
|
-
data =
|
|
250
|
+
data = self.__fget__()
|
|
270
251
|
data.symmetric_difference_update(other)
|
|
271
|
-
self.__fset__(
|
|
252
|
+
self.__fset__(data)
|
|
272
253
|
|
|
273
254
|
@setdoc.basic
|
|
274
255
|
def update(self: Self, /, *others: abc.Iterable[Item]) -> None:
|
|
275
256
|
self.__fset__(self.__fget__().union(*others))
|
|
276
257
|
|
|
277
258
|
|
|
278
|
-
class HoldSet[Item: abc.Hashable](
|
|
279
|
-
DataSet[Item],
|
|
280
|
-
BaseHoldCollection[Item],
|
|
281
|
-
):
|
|
282
|
-
"""Provide usable mutable set-like with slots."""
|
|
283
|
-
|
|
284
|
-
__slots__ = ()
|
|
285
|
-
|
|
286
|
-
@setdoc.basic
|
|
287
|
-
def __fget__(self: Self) -> HoldSet.Data[Item]:
|
|
288
|
-
return set(self._data)
|
|
289
|
-
|
|
290
|
-
@setdoc.basic
|
|
291
|
-
def __fset__(self: Self, value: HoldSet.Data[Item], /) -> None:
|
|
292
|
-
self._data: HoldSet.Data[Item] = value
|
|
293
|
-
|
|
294
|
-
|
|
295
259
|
### MAPPING ###
|
|
296
260
|
class BaseDataMapping[Key, Value](
|
|
297
261
|
BaseDataCollection[Key], abc.Mapping[Key, Value]
|
|
@@ -337,6 +301,10 @@ class BaseDataDict[Key: abc.Hashable, Value](
|
|
|
337
301
|
| abc.Iterable[tuple[DataKey | str, Optional[DataValue]]]
|
|
338
302
|
)
|
|
339
303
|
|
|
304
|
+
@abstractmethod
|
|
305
|
+
@setdoc.basic
|
|
306
|
+
def __fget__(self: Self) -> Data[Key, Value]: ...
|
|
307
|
+
|
|
340
308
|
@abstractmethod
|
|
341
309
|
@setdoc.basic
|
|
342
310
|
def __fset__(self: Self, data: Data[Key, Value], /) -> None: ...
|
|
@@ -369,10 +337,6 @@ class BaseDataDict[Key: abc.Hashable, Value](
|
|
|
369
337
|
|
|
370
338
|
# __ror__ is unnecessary because of how __or__ is defined
|
|
371
339
|
|
|
372
|
-
@abstractmethod
|
|
373
|
-
@setdoc.basic
|
|
374
|
-
def __fget__(self: Self) -> Data[Key, Value]: ...
|
|
375
|
-
|
|
376
340
|
@classmethod
|
|
377
341
|
@setdoc.basic
|
|
378
342
|
def fromkeys(
|
|
@@ -397,23 +361,6 @@ class FrozenDataDict[Key: abc.Hashable, Value](
|
|
|
397
361
|
return hash(frozendict(self.__fget__()))
|
|
398
362
|
|
|
399
363
|
|
|
400
|
-
class FrozenHoldDict[Key: abc.Hashable, Value](
|
|
401
|
-
FrozenDataDict[Key, Value],
|
|
402
|
-
BaseHoldCollection[Key | str],
|
|
403
|
-
):
|
|
404
|
-
"""Provide usable frozen dict-like with slots."""
|
|
405
|
-
|
|
406
|
-
__slots__ = ()
|
|
407
|
-
|
|
408
|
-
@setdoc.basic
|
|
409
|
-
def __fget__(self: Self) -> FrozenHoldDict.Data[Key, Value]:
|
|
410
|
-
return dict(self._data)
|
|
411
|
-
|
|
412
|
-
@setdoc.basic
|
|
413
|
-
def __fset__(self: Self, data: FrozenHoldDict.Data[Key, Value], /) -> None:
|
|
414
|
-
self._data: FrozenHoldDict.Data[Key, Value] = data
|
|
415
|
-
|
|
416
|
-
|
|
417
364
|
class DataDict[Key: abc.Hashable, Value](
|
|
418
365
|
BaseDataDict[Key, Value],
|
|
419
366
|
abc.MutableMapping[Key | str, Optional[Value]],
|
|
@@ -429,14 +376,6 @@ class DataDict[Key: abc.Hashable, Value](
|
|
|
429
376
|
del data[key]
|
|
430
377
|
self.__fset__(data)
|
|
431
378
|
|
|
432
|
-
@abstractmethod
|
|
433
|
-
@setdoc.basic
|
|
434
|
-
def __fget__(self: Self) -> DataDict.Data[Key, Value]: ...
|
|
435
|
-
|
|
436
|
-
@abstractmethod
|
|
437
|
-
@setdoc.basic
|
|
438
|
-
def __fset__(self: Self, data: DataDict.Data[Key, Value], /) -> None: ...
|
|
439
|
-
|
|
440
379
|
@setdoc.basic
|
|
441
380
|
def __ior__(
|
|
442
381
|
self: Self,
|
|
@@ -464,27 +403,6 @@ class DataDict[Key: abc.Hashable, Value](
|
|
|
464
403
|
return type(self)(self)
|
|
465
404
|
|
|
466
405
|
|
|
467
|
-
class HoldDict[Key: abc.Hashable, Value](
|
|
468
|
-
DataDict[Key, Value],
|
|
469
|
-
BaseHoldCollection[Key | str],
|
|
470
|
-
):
|
|
471
|
-
"""Provide usable mutable dict-like with slots."""
|
|
472
|
-
|
|
473
|
-
__slots__ = ()
|
|
474
|
-
|
|
475
|
-
@setdoc.basic
|
|
476
|
-
def __fget__(self: Self) -> HoldDict.Data[Key, Value]:
|
|
477
|
-
return dict(self._data)
|
|
478
|
-
|
|
479
|
-
@setdoc.basic
|
|
480
|
-
def __fset__(
|
|
481
|
-
self: Self,
|
|
482
|
-
value: HoldDict.Data[Key, Value],
|
|
483
|
-
/,
|
|
484
|
-
) -> None:
|
|
485
|
-
self._data: HoldDict.Data[Key, Value] = value
|
|
486
|
-
|
|
487
|
-
|
|
488
406
|
### SEQUENCE ###
|
|
489
407
|
|
|
490
408
|
|
|
@@ -568,7 +486,11 @@ class BaseDataList[Item](BaseDataSequence[Item]):
|
|
|
568
486
|
|
|
569
487
|
@abstractmethod
|
|
570
488
|
@setdoc.basic
|
|
571
|
-
def
|
|
489
|
+
def __fget__(self: Self) -> Data[Item]: ...
|
|
490
|
+
|
|
491
|
+
@abstractmethod
|
|
492
|
+
@setdoc.basic
|
|
493
|
+
def __fset__(self: Self, data: Data[Item], /) -> None: ...
|
|
572
494
|
|
|
573
495
|
@setdoc.basic
|
|
574
496
|
def __ge__(self: Self, other: object, /) -> NotImplementedType | bool:
|
|
@@ -629,10 +551,6 @@ class BaseDataList[Item](BaseDataSequence[Item]):
|
|
|
629
551
|
|
|
630
552
|
__rmul__ = __mul__
|
|
631
553
|
|
|
632
|
-
@abstractmethod
|
|
633
|
-
@setdoc.basic
|
|
634
|
-
def __fget__(self: Self) -> Data[Item]: ...
|
|
635
|
-
|
|
636
554
|
|
|
637
555
|
class FrozenDataList[Item](
|
|
638
556
|
BaseDataList[Item],
|
|
@@ -647,23 +565,6 @@ class FrozenDataList[Item](
|
|
|
647
565
|
return hash(tuple(self.__fget__()))
|
|
648
566
|
|
|
649
567
|
|
|
650
|
-
class FrozenHoldList[Item](
|
|
651
|
-
FrozenDataList[Item],
|
|
652
|
-
BaseHoldCollection[Item],
|
|
653
|
-
):
|
|
654
|
-
"""Provide usable frozen list-like with slots."""
|
|
655
|
-
|
|
656
|
-
__slots__ = ()
|
|
657
|
-
|
|
658
|
-
@setdoc.basic
|
|
659
|
-
def __fget__(self: Self) -> FrozenHoldList.Data[Item]:
|
|
660
|
-
return list(self._data)
|
|
661
|
-
|
|
662
|
-
@setdoc.basic
|
|
663
|
-
def __fset__(self: Self, data: FrozenHoldList.Data[Item], /) -> None:
|
|
664
|
-
self._data: FrozenHoldList.Data[Item] = data
|
|
665
|
-
|
|
666
|
-
|
|
667
568
|
class DataList[Item](
|
|
668
569
|
BaseDataList[Item],
|
|
669
570
|
abc.MutableSequence[Item],
|
|
@@ -681,18 +582,6 @@ class DataList[Item](
|
|
|
681
582
|
del data[other]
|
|
682
583
|
self.__fset__(data)
|
|
683
584
|
|
|
684
|
-
@abstractmethod
|
|
685
|
-
@setdoc.basic
|
|
686
|
-
def __fget__(self: Self) -> DataList.Data[Item]: ...
|
|
687
|
-
|
|
688
|
-
@abstractmethod
|
|
689
|
-
@setdoc.basic
|
|
690
|
-
def __fset__(
|
|
691
|
-
self: Self,
|
|
692
|
-
value: DataList.Data[Item],
|
|
693
|
-
/,
|
|
694
|
-
) -> None: ...
|
|
695
|
-
|
|
696
585
|
@setdoc.basic
|
|
697
586
|
def __imul__(self: Self, other: SupportsIndex, /) -> Self:
|
|
698
587
|
self.__fset__(self.__fget__() * other)
|
|
@@ -742,20 +631,3 @@ class DataList[Item](
|
|
|
742
631
|
data = self.__fget__()
|
|
743
632
|
data.sort(key=key, reverse=reverse)
|
|
744
633
|
self.__fset__(data)
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
class HoldList[Item](
|
|
748
|
-
DataList[Item],
|
|
749
|
-
BaseHoldCollection[Item],
|
|
750
|
-
):
|
|
751
|
-
"""Provide usable mutable list-like with slots."""
|
|
752
|
-
|
|
753
|
-
__slots__ = ()
|
|
754
|
-
|
|
755
|
-
@setdoc.basic
|
|
756
|
-
def __fget__(self: Self) -> HoldList.Data[Item]:
|
|
757
|
-
return list(self._data)
|
|
758
|
-
|
|
759
|
-
@setdoc.basic
|
|
760
|
-
def __fset__(self: Self, value: HoldList.Data[Item], /) -> None:
|
|
761
|
-
self._data: HoldList.Data[Item] = value
|
|
@@ -25,9 +25,5 @@ tests/test_FrozenHoldSet.py
|
|
|
25
25
|
tests/test_HoldDict.py
|
|
26
26
|
tests/test_HoldList.py
|
|
27
27
|
tests/test_HoldSet.py
|
|
28
|
-
tests/test_datahold_TestCopy.py
|
|
29
|
-
tests/test_datahold_TestDataAttribute.py
|
|
30
|
-
tests/test_datahold_TestFrozenMutability.py
|
|
31
|
-
tests/test_datahold_TestMutableBehavior.py
|
|
32
28
|
tests/test_testdata.py
|
|
33
29
|
tests/testdata.toml
|
|
@@ -1,11 +1,55 @@
|
|
|
1
|
-
__all__: list[str] = [
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestCopy",
|
|
3
|
+
"TestDataAttribute",
|
|
4
|
+
"TestFrozenHoldDict",
|
|
5
|
+
"TestFrozenMutability",
|
|
6
|
+
]
|
|
2
7
|
|
|
3
8
|
import unittest
|
|
9
|
+
from collections import abc
|
|
4
10
|
from typing import Any, Self
|
|
5
11
|
|
|
6
12
|
from frozendict import frozendict
|
|
7
13
|
|
|
8
|
-
from datahold import
|
|
14
|
+
from datahold import BaseHoldObject, FrozenDataDict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FrozenHoldDict[Key: abc.Hashable, Value](
|
|
18
|
+
BaseHoldObject[FrozenDataDict.Data[Key, Value]],
|
|
19
|
+
FrozenDataDict[Key, Value],
|
|
20
|
+
):
|
|
21
|
+
"""Provide usable frozen dict-like with slots."""
|
|
22
|
+
|
|
23
|
+
__slots__ = ()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestCopy(unittest.TestCase):
|
|
27
|
+
|
|
28
|
+
def test_frozen_have_no_copy_2(self: Self) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Frozen classes should not define their own copy method.
|
|
31
|
+
(If a parent class or wrapped object exposes one, we ignore that.)
|
|
32
|
+
"""
|
|
33
|
+
args: Any
|
|
34
|
+
cls: Any
|
|
35
|
+
copy_obj: Any
|
|
36
|
+
obj: Any
|
|
37
|
+
cls, args = (FrozenHoldDict, ({"a": 1},))
|
|
38
|
+
# They must not *define* copy themselves
|
|
39
|
+
self.assertNotIn("copy", cls.__dict__)
|
|
40
|
+
# Optional: if they *do* expose copy on the instance, it should not
|
|
41
|
+
# create a mutable variant; you can drop this if you prefer.
|
|
42
|
+
obj = cls(*args)
|
|
43
|
+
if hasattr(obj, "copy"):
|
|
44
|
+
copy_obj = obj.copy()
|
|
45
|
+
self.assertIsInstance(copy_obj, cls)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestDataAttribute(unittest.TestCase):
|
|
49
|
+
def test_dict_data_is_immutable_mapping(self: Self) -> None:
|
|
50
|
+
f: FrozenHoldDict[Any, Any]
|
|
51
|
+
f = FrozenHoldDict({"a": 1})
|
|
52
|
+
self.assertIsInstance(f.__fget__(), dict)
|
|
9
53
|
|
|
10
54
|
|
|
11
55
|
class TestFrozenHoldDict(unittest.TestCase):
|
|
@@ -127,5 +171,15 @@ class TestFrozenHoldDict(unittest.TestCase):
|
|
|
127
171
|
)
|
|
128
172
|
|
|
129
173
|
|
|
174
|
+
class TestFrozenMutability(unittest.TestCase):
|
|
175
|
+
def test_frozen_dict_cannot_mutate(self: Self) -> None:
|
|
176
|
+
f: Any
|
|
177
|
+
f = FrozenHoldDict({"a": 1})
|
|
178
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
179
|
+
f["b"] = 2
|
|
180
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
181
|
+
f.pop("a", None)
|
|
182
|
+
|
|
183
|
+
|
|
130
184
|
if __name__ == "__main__":
|
|
131
185
|
unittest.main()
|
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
__all__: list[str] = [
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestCopy",
|
|
3
|
+
"TestDataAttribute",
|
|
4
|
+
"TestFrozenHoldLists",
|
|
5
|
+
"TestFrozenMutability",
|
|
6
|
+
]
|
|
2
7
|
import unittest
|
|
3
8
|
from typing import Any, Self
|
|
4
9
|
|
|
5
|
-
from datahold import
|
|
10
|
+
from datahold import BaseHoldObject, FrozenDataList
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FrozenHoldList[Item](
|
|
14
|
+
BaseHoldObject[FrozenDataList.Data[Item]],
|
|
15
|
+
FrozenDataList[Item],
|
|
16
|
+
):
|
|
17
|
+
"""Provide usable frozen list-like with slots."""
|
|
18
|
+
|
|
19
|
+
__slots__ = ()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestCopy(unittest.TestCase):
|
|
23
|
+
|
|
24
|
+
def test_frozen_have_no_copy_2(self: Self) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Frozen classes should not define their own copy method.
|
|
27
|
+
(If a parent class or wrapped object exposes one, we ignore that.)
|
|
28
|
+
"""
|
|
29
|
+
args: Any
|
|
30
|
+
cls: Any
|
|
31
|
+
copy_obj: Any
|
|
32
|
+
obj: Any
|
|
33
|
+
cls, args = (FrozenHoldList, ([1, 2],))
|
|
34
|
+
# They must not *define* copy themselves
|
|
35
|
+
self.assertNotIn("copy", cls.__dict__)
|
|
36
|
+
# Optional: if they *do* expose copy on the instance, it should not
|
|
37
|
+
# create a mutable variant; you can drop this if you prefer.
|
|
38
|
+
obj = cls(*args)
|
|
39
|
+
if hasattr(obj, "copy"):
|
|
40
|
+
copy_obj = obj.copy()
|
|
41
|
+
self.assertIsInstance(copy_obj, cls)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestDataAttribute(unittest.TestCase):
|
|
45
|
+
def test_list_data_is_tuple(self: Self) -> None:
|
|
46
|
+
f: FrozenHoldList[Any]
|
|
47
|
+
f = FrozenHoldList([1, 2, 3])
|
|
48
|
+
self.assertIsInstance(f.__fget__(), list)
|
|
6
49
|
|
|
7
50
|
|
|
8
51
|
class TestFrozenHoldList(unittest.TestCase):
|
|
@@ -144,5 +187,15 @@ class TestFrozenHoldList(unittest.TestCase):
|
|
|
144
187
|
self.obj.index(99)
|
|
145
188
|
|
|
146
189
|
|
|
190
|
+
class TestFrozenMutability(unittest.TestCase):
|
|
191
|
+
def test_frozen_list_cannot_mutate(self: Self) -> None:
|
|
192
|
+
f: Any
|
|
193
|
+
f = FrozenHoldList([1, 2, 3])
|
|
194
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
195
|
+
f.append(4)
|
|
196
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
197
|
+
f.pop()
|
|
198
|
+
|
|
199
|
+
|
|
147
200
|
if __name__ == "__main__":
|
|
148
201
|
unittest.main()
|
|
@@ -1,8 +1,54 @@
|
|
|
1
|
-
__all__: list[str] = [
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestCopy",
|
|
3
|
+
"TestDataAttribute",
|
|
4
|
+
"TestFrozenHoldSet",
|
|
5
|
+
"TestFrozenMutability",
|
|
6
|
+
]
|
|
2
7
|
import unittest
|
|
3
|
-
from
|
|
8
|
+
from collections import abc
|
|
9
|
+
from typing import Any, Self
|
|
4
10
|
|
|
5
|
-
from datahold import
|
|
11
|
+
from datahold import BaseHoldObject, FrozenDataSet
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FrozenHoldSet[Item: abc.Hashable](
|
|
15
|
+
BaseHoldObject[FrozenDataSet.Data[Item]],
|
|
16
|
+
FrozenDataSet[Item],
|
|
17
|
+
):
|
|
18
|
+
"""Provide usable frozen set-like with slots."""
|
|
19
|
+
|
|
20
|
+
__slots__ = ()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestCopy(unittest.TestCase):
|
|
24
|
+
|
|
25
|
+
def test_frozen_have_no_copy_2(self: Self) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Frozen classes should not define their own copy method.
|
|
28
|
+
(If a parent class or wrapped object exposes one, we ignore that.)
|
|
29
|
+
"""
|
|
30
|
+
args: Any
|
|
31
|
+
cls: Any
|
|
32
|
+
copy_obj: Any
|
|
33
|
+
obj: Any
|
|
34
|
+
cls = FrozenHoldSet
|
|
35
|
+
args = ({1, 2},)
|
|
36
|
+
# They must not *define* copy themselves
|
|
37
|
+
self.assertNotIn("copy", cls.__dict__)
|
|
38
|
+
|
|
39
|
+
# Optional: if they *do* expose copy on the instance, it should not
|
|
40
|
+
# create a mutable variant; you can drop this if you prefer.
|
|
41
|
+
obj = cls(*args)
|
|
42
|
+
if hasattr(obj, "copy"):
|
|
43
|
+
copy_obj = obj.copy()
|
|
44
|
+
self.assertIsInstance(copy_obj, cls)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestDataAttribute(unittest.TestCase):
|
|
48
|
+
def test_set_data_is_frozenset(self: Self) -> None:
|
|
49
|
+
f: FrozenHoldSet[Any]
|
|
50
|
+
f = FrozenHoldSet({1, 2, 3})
|
|
51
|
+
self.assertIsInstance(f.__fget__(), set)
|
|
6
52
|
|
|
7
53
|
|
|
8
54
|
class TestFrozenHoldSet(unittest.TestCase):
|
|
@@ -185,5 +231,16 @@ class TestFrozenHoldSet(unittest.TestCase):
|
|
|
185
231
|
)
|
|
186
232
|
|
|
187
233
|
|
|
234
|
+
class TestFrozenMutability(unittest.TestCase):
|
|
235
|
+
|
|
236
|
+
def test_frozen_set_cannot_mutate(self: Self) -> None:
|
|
237
|
+
f: Any
|
|
238
|
+
f = FrozenHoldSet({1, 2, 3})
|
|
239
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
240
|
+
f.add(4)
|
|
241
|
+
with self.assertRaises((TypeError, AttributeError)):
|
|
242
|
+
f.remove(1)
|
|
243
|
+
|
|
244
|
+
|
|
188
245
|
if __name__ == "__main__":
|
|
189
246
|
unittest.main()
|
|
@@ -1,8 +1,45 @@
|
|
|
1
|
-
__all__: list[str] = [
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestDataAttribute",
|
|
3
|
+
"TestHoldDict",
|
|
4
|
+
"TestMutableBehavior",
|
|
5
|
+
"TestCopy",
|
|
6
|
+
]
|
|
2
7
|
import unittest
|
|
3
|
-
from
|
|
8
|
+
from collections import abc
|
|
9
|
+
from typing import Any, Self, cast
|
|
4
10
|
|
|
5
|
-
from datahold import
|
|
11
|
+
from datahold import BaseHoldObject, DataDict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HoldDict[Key: abc.Hashable, Value](
|
|
15
|
+
BaseHoldObject[DataDict.Data[Key, Value]],
|
|
16
|
+
DataDict[Key, Value],
|
|
17
|
+
):
|
|
18
|
+
"""Provide usable mutable dict-like with slots."""
|
|
19
|
+
|
|
20
|
+
__slots__ = ()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestCopy(unittest.TestCase):
|
|
24
|
+
def test_mutable_copy_returns_same_type_and_is_shallow(self: Self) -> None:
|
|
25
|
+
d: HoldDict[str, dict[str, int]]
|
|
26
|
+
d_copy: HoldDict[str, dict[str, int]]
|
|
27
|
+
d = HoldDict({"a": {"x": 1}})
|
|
28
|
+
d_copy = d.copy()
|
|
29
|
+
self.assertIsInstance(d_copy, type(d))
|
|
30
|
+
self.assertIsNot(d_copy, d)
|
|
31
|
+
self.assertEqual(dict(d_copy), dict(d))
|
|
32
|
+
|
|
33
|
+
# shallow: inner object is shared
|
|
34
|
+
cast(dict[str, int], d["a"])["x"] = 2
|
|
35
|
+
self.assertEqual(cast(dict[str, int], d_copy["a"])["x"], 2)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestDataAttribute(unittest.TestCase):
|
|
39
|
+
def test_dict_data_is_immutable_mapping(self: Self) -> None:
|
|
40
|
+
m: HoldDict[Any, Any]
|
|
41
|
+
m = HoldDict({"a": 1})
|
|
42
|
+
self.assertIsInstance(m.__fget__(), dict)
|
|
6
43
|
|
|
7
44
|
|
|
8
45
|
class TestHoldDict(unittest.TestCase):
|
|
@@ -164,5 +201,14 @@ class TestHoldDict(unittest.TestCase):
|
|
|
164
201
|
self.assertIsNot(result, self.obj)
|
|
165
202
|
|
|
166
203
|
|
|
204
|
+
class TestMutableBehavior(unittest.TestCase):
|
|
205
|
+
def test_hold_dict_mutates_and_syncs_data(self: Self) -> None:
|
|
206
|
+
x: HoldDict[Any, Any]
|
|
207
|
+
x = HoldDict({"a": 1})
|
|
208
|
+
x["b"] = 2
|
|
209
|
+
self.assertEqual(x["b"], 2)
|
|
210
|
+
self.assertEqual(x.__fget__()["b"], 2)
|
|
211
|
+
|
|
212
|
+
|
|
167
213
|
if __name__ == "__main__":
|
|
168
214
|
unittest.main()
|