datahold 4.0.0.dev6__tar.gz → 4.0.0.dev8__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.dev6 → datahold-4.0.0.dev8}/PKG-INFO +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/pyproject.toml +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold/__init__.py +59 -152
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/SOURCES.txt +0 -4
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldDict.py +56 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldList.py +55 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldSet.py +60 -3
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldDict.py +49 -3
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldList.py +48 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldSet.py +48 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_testdata.py +23 -4
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/testdata.toml +20 -134
- datahold-4.0.0.dev6/tests/test_datahold_TestCopy.py +0 -81
- datahold-4.0.0.dev6/tests/test_datahold_TestDataAttribute.py +0 -46
- datahold-4.0.0.dev6/tests/test_datahold_TestFrozenMutability.py +0 -36
- datahold-4.0.0.dev6/tests/test_datahold_TestMutableBehavior.py +0 -33
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/README.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/run_tests.py +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/setup.cfg +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/top_level.txt +0 -0
|
@@ -10,19 +10,14 @@ __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
|
+
"FrozenDataMapping",
|
|
19
20
|
"FrozenDataSet",
|
|
20
|
-
"FrozenHoldDict",
|
|
21
|
-
"FrozenHoldList",
|
|
22
|
-
"FrozenHoldSet",
|
|
23
|
-
"HoldDict",
|
|
24
|
-
"HoldList",
|
|
25
|
-
"HoldSet",
|
|
26
21
|
]
|
|
27
22
|
|
|
28
23
|
import enum
|
|
@@ -45,6 +40,11 @@ from frozendict import frozendict
|
|
|
45
40
|
### UTILS ###
|
|
46
41
|
|
|
47
42
|
|
|
43
|
+
class Copyable(Protocol):
|
|
44
|
+
@setdoc.basic
|
|
45
|
+
def copy(self: Self) -> Self: ...
|
|
46
|
+
|
|
47
|
+
|
|
48
48
|
class Missing(enum.Enum):
|
|
49
49
|
missing = None
|
|
50
50
|
|
|
@@ -61,6 +61,23 @@ class SupportsKeysAndGetitem[Key, Value](Protocol):
|
|
|
61
61
|
def keys(self: Self) -> abc.Iterable[Key]: ...
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
### OBJECT ###
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class BaseHoldObject[HoldData: Copyable]:
|
|
68
|
+
"""Provide abc for usable classes with slots."""
|
|
69
|
+
|
|
70
|
+
__slots__ = ("_data",)
|
|
71
|
+
|
|
72
|
+
@setdoc.basic
|
|
73
|
+
def __fget__(self: Self) -> HoldData:
|
|
74
|
+
return self._data.copy()
|
|
75
|
+
|
|
76
|
+
@setdoc.basic
|
|
77
|
+
def __fset__(self: Self, data: HoldData, /) -> None:
|
|
78
|
+
self._data: HoldData = data
|
|
79
|
+
|
|
80
|
+
|
|
64
81
|
### COLLECTION ###
|
|
65
82
|
|
|
66
83
|
|
|
@@ -90,6 +107,10 @@ class BaseDataCollection[Item](
|
|
|
90
107
|
except TypeError:
|
|
91
108
|
return other in (x for x in self.__fget__()) # type: ignore[operator]
|
|
92
109
|
|
|
110
|
+
@abstractmethod
|
|
111
|
+
@setdoc.basic
|
|
112
|
+
def __fget__(self: Self) -> Data[Item]: ...
|
|
113
|
+
|
|
93
114
|
@setdoc.basic
|
|
94
115
|
def __iter__(self: Self, /) -> abc.Iterator[Item]:
|
|
95
116
|
return iter(self.__fget__())
|
|
@@ -98,16 +119,6 @@ class BaseDataCollection[Item](
|
|
|
98
119
|
def __len__(self: Self, /) -> int:
|
|
99
120
|
return len(self.__fget__())
|
|
100
121
|
|
|
101
|
-
@abstractmethod
|
|
102
|
-
@setdoc.basic
|
|
103
|
-
def __fget__(self: Self) -> Data[Item]: ...
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class BaseHoldCollection[Item](BaseDataCollection[Item]):
|
|
107
|
-
"""Provide abc for usable classes with slots."""
|
|
108
|
-
|
|
109
|
-
__slots__ = ("_data",)
|
|
110
|
-
|
|
111
122
|
|
|
112
123
|
### ABSTRACT SET ###
|
|
113
124
|
|
|
@@ -189,23 +200,6 @@ class FrozenDataSet[Item: abc.Hashable](BaseDataSet[Item], abc.Hashable):
|
|
|
189
200
|
return hash(frozenset(self.__fget__()))
|
|
190
201
|
|
|
191
202
|
|
|
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
203
|
class DataSet[Item: abc.Hashable](
|
|
210
204
|
BaseDataSet[Item],
|
|
211
205
|
abc.MutableSet[Item],
|
|
@@ -216,7 +210,10 @@ class DataSet[Item: abc.Hashable](
|
|
|
216
210
|
|
|
217
211
|
@setdoc.basic
|
|
218
212
|
def add(self: Self, item: Item, /) -> None:
|
|
219
|
-
|
|
213
|
+
data: set[Item]
|
|
214
|
+
data = self.__fget__()
|
|
215
|
+
data.add(item)
|
|
216
|
+
self.__fset__(data)
|
|
220
217
|
|
|
221
218
|
@setdoc.basic
|
|
222
219
|
def copy(self: Self) -> Self:
|
|
@@ -228,60 +225,34 @@ class DataSet[Item: abc.Hashable](
|
|
|
228
225
|
/,
|
|
229
226
|
*others: abc.Iterable[abc.Hashable],
|
|
230
227
|
) -> None:
|
|
231
|
-
|
|
232
|
-
data = set(self.__fget__())
|
|
233
|
-
data.difference_update(*others)
|
|
234
|
-
self.__fset__(set(data))
|
|
228
|
+
self.__fset__(self.__fget__().difference(*others))
|
|
235
229
|
|
|
236
230
|
@setdoc.basic
|
|
237
231
|
def discard(self: Self, item: abc.Hashable, /) -> None:
|
|
238
232
|
data: set[Item]
|
|
239
|
-
data =
|
|
233
|
+
data = self.__fget__()
|
|
240
234
|
data.discard(item)
|
|
241
|
-
self.__fset__(
|
|
235
|
+
self.__fset__(data)
|
|
242
236
|
|
|
243
237
|
@setdoc.basic
|
|
244
238
|
def intersection_update(
|
|
245
239
|
self: Self, /, *others: abc.Iterable[abc.Hashable]
|
|
246
240
|
) -> None:
|
|
247
|
-
|
|
248
|
-
data = set(self.__fget__())
|
|
249
|
-
data.intersection_update(*others)
|
|
250
|
-
self.__fset__(set(data))
|
|
241
|
+
self.__fset__(self.__fget__().intersection(*others))
|
|
251
242
|
|
|
252
243
|
@setdoc.basic
|
|
253
244
|
def symmetric_difference_update(
|
|
254
245
|
self: Self, other: abc.Iterable[Item], /
|
|
255
246
|
) -> None:
|
|
256
|
-
|
|
257
|
-
data = set(self.__fget__())
|
|
258
|
-
data.symmetric_difference_update(other)
|
|
259
|
-
self.__fset__(set(data))
|
|
247
|
+
self.__fset__(self.__fget__().symmetric_difference(other))
|
|
260
248
|
|
|
261
249
|
@setdoc.basic
|
|
262
250
|
def update(self: Self, /, *others: abc.Iterable[Item]) -> None:
|
|
263
251
|
self.__fset__(self.__fget__().union(*others))
|
|
264
252
|
|
|
265
253
|
|
|
266
|
-
class HoldSet[Item: abc.Hashable](
|
|
267
|
-
DataSet[Item],
|
|
268
|
-
BaseHoldCollection[Item],
|
|
269
|
-
):
|
|
270
|
-
"""Provide usable mutable set-like with slots."""
|
|
271
|
-
|
|
272
|
-
__slots__ = ()
|
|
273
|
-
|
|
274
|
-
@setdoc.basic
|
|
275
|
-
def __fget__(self: Self) -> HoldSet.Data[Item]:
|
|
276
|
-
return set(self._data)
|
|
277
|
-
|
|
278
|
-
@setdoc.basic
|
|
279
|
-
def __fset__(self: Self, value: HoldSet.Data[Item], /) -> None:
|
|
280
|
-
self._data: HoldSet.Data[Item] = value
|
|
281
|
-
|
|
282
|
-
|
|
283
254
|
### MAPPING ###
|
|
284
|
-
class BaseDataMapping[Key, Value](
|
|
255
|
+
class BaseDataMapping[Key: abc.Hashable, Value](
|
|
285
256
|
BaseDataCollection[Key], abc.Mapping[Key, Value]
|
|
286
257
|
):
|
|
287
258
|
"""Provide an easy abc for a custom mapping."""
|
|
@@ -294,8 +265,11 @@ class BaseDataMapping[Key, Value](
|
|
|
294
265
|
Protocol,
|
|
295
266
|
):
|
|
296
267
|
@setdoc.basic
|
|
297
|
-
def __getitem__(self: Self, key: Never, /) -> DataValue:
|
|
298
|
-
|
|
268
|
+
def __getitem__(self: Self, key: Never, /) -> DataValue: ...
|
|
269
|
+
|
|
270
|
+
@abstractmethod
|
|
271
|
+
@setdoc.basic
|
|
272
|
+
def __fget__(self: Self) -> Data[Key, Value]: ...
|
|
299
273
|
|
|
300
274
|
@setdoc.basic
|
|
301
275
|
def __getitem__(self: Self, key: object, /) -> Value:
|
|
@@ -304,9 +278,18 @@ class BaseDataMapping[Key, Value](
|
|
|
304
278
|
except TypeError:
|
|
305
279
|
raise KeyError(key) from None
|
|
306
280
|
|
|
307
|
-
|
|
281
|
+
|
|
282
|
+
class FrozenDataMapping[Key: abc.Hashable, Value](
|
|
283
|
+
BaseDataMapping[Key, Value],
|
|
284
|
+
abc.Hashable,
|
|
285
|
+
):
|
|
286
|
+
"""Provide an easy abc for a custom frozen mapping."""
|
|
287
|
+
|
|
288
|
+
__slots__ = ()
|
|
289
|
+
|
|
308
290
|
@setdoc.basic
|
|
309
|
-
def
|
|
291
|
+
def __hash__(self: Self) -> int:
|
|
292
|
+
return hash(frozendict(self.items()))
|
|
310
293
|
|
|
311
294
|
|
|
312
295
|
### DICT ###
|
|
@@ -374,33 +357,12 @@ class BaseDataDict[Key: abc.Hashable, Value](
|
|
|
374
357
|
|
|
375
358
|
class FrozenDataDict[Key: abc.Hashable, Value](
|
|
376
359
|
BaseDataDict[Key, Value],
|
|
377
|
-
|
|
360
|
+
FrozenDataMapping[Key | str, Optional[Value]],
|
|
378
361
|
):
|
|
379
362
|
"""Provide easy abc for custom frozen dict-like."""
|
|
380
363
|
|
|
381
364
|
__slots__ = ()
|
|
382
365
|
|
|
383
|
-
@setdoc.basic
|
|
384
|
-
def __hash__(self: Self) -> int:
|
|
385
|
-
return hash(frozendict(self.__fget__()))
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
class FrozenHoldDict[Key: abc.Hashable, Value](
|
|
389
|
-
FrozenDataDict[Key, Value],
|
|
390
|
-
BaseHoldCollection[Key | str],
|
|
391
|
-
):
|
|
392
|
-
"""Provide usable frozen dict-like with slots."""
|
|
393
|
-
|
|
394
|
-
__slots__ = ()
|
|
395
|
-
|
|
396
|
-
@setdoc.basic
|
|
397
|
-
def __fget__(self: Self) -> FrozenHoldDict.Data[Key, Value]:
|
|
398
|
-
return dict(self._data)
|
|
399
|
-
|
|
400
|
-
@setdoc.basic
|
|
401
|
-
def __fset__(self: Self, data: FrozenHoldDict.Data[Key, Value], /) -> None:
|
|
402
|
-
self._data: FrozenHoldDict.Data[Key, Value] = data
|
|
403
|
-
|
|
404
366
|
|
|
405
367
|
class DataDict[Key: abc.Hashable, Value](
|
|
406
368
|
BaseDataDict[Key, Value],
|
|
@@ -444,27 +406,6 @@ class DataDict[Key: abc.Hashable, Value](
|
|
|
444
406
|
return type(self)(self)
|
|
445
407
|
|
|
446
408
|
|
|
447
|
-
class HoldDict[Key: abc.Hashable, Value](
|
|
448
|
-
DataDict[Key, Value],
|
|
449
|
-
BaseHoldCollection[Key | str],
|
|
450
|
-
):
|
|
451
|
-
"""Provide usable mutable dict-like with slots."""
|
|
452
|
-
|
|
453
|
-
__slots__ = ()
|
|
454
|
-
|
|
455
|
-
@setdoc.basic
|
|
456
|
-
def __fget__(self: Self) -> HoldDict.Data[Key, Value]:
|
|
457
|
-
return dict(self._data)
|
|
458
|
-
|
|
459
|
-
@setdoc.basic
|
|
460
|
-
def __fset__(
|
|
461
|
-
self: Self,
|
|
462
|
-
value: HoldDict.Data[Key, Value],
|
|
463
|
-
/,
|
|
464
|
-
) -> None:
|
|
465
|
-
self._data: HoldDict.Data[Key, Value] = value
|
|
466
|
-
|
|
467
|
-
|
|
468
409
|
### SEQUENCE ###
|
|
469
410
|
|
|
470
411
|
|
|
@@ -496,6 +437,10 @@ class BaseDataSequence[Item](
|
|
|
496
437
|
self: Self, key: int | Slice[int], /
|
|
497
438
|
) -> DataItem | abc.Sequence[DataItem]: ...
|
|
498
439
|
|
|
440
|
+
@abstractmethod
|
|
441
|
+
@setdoc.basic
|
|
442
|
+
def __fget__(self: Self) -> Data[Item]: ...
|
|
443
|
+
|
|
499
444
|
@overload
|
|
500
445
|
@setdoc.basic
|
|
501
446
|
def __getitem__(self: Self, key: int, /) -> Item: ...
|
|
@@ -508,10 +453,6 @@ class BaseDataSequence[Item](
|
|
|
508
453
|
) -> Item | abc.Sequence[Item]:
|
|
509
454
|
return self.__fget__()[key]
|
|
510
455
|
|
|
511
|
-
@abstractmethod
|
|
512
|
-
@setdoc.basic
|
|
513
|
-
def __fget__(self: Self) -> Data[Item]: ...
|
|
514
|
-
|
|
515
456
|
|
|
516
457
|
### LIST ###
|
|
517
458
|
|
|
@@ -627,23 +568,6 @@ class FrozenDataList[Item](
|
|
|
627
568
|
return hash(tuple(self.__fget__()))
|
|
628
569
|
|
|
629
570
|
|
|
630
|
-
class FrozenHoldList[Item](
|
|
631
|
-
FrozenDataList[Item],
|
|
632
|
-
BaseHoldCollection[Item],
|
|
633
|
-
):
|
|
634
|
-
"""Provide usable frozen list-like with slots."""
|
|
635
|
-
|
|
636
|
-
__slots__ = ()
|
|
637
|
-
|
|
638
|
-
@setdoc.basic
|
|
639
|
-
def __fget__(self: Self) -> FrozenHoldList.Data[Item]:
|
|
640
|
-
return list(self._data)
|
|
641
|
-
|
|
642
|
-
@setdoc.basic
|
|
643
|
-
def __fset__(self: Self, data: FrozenHoldList.Data[Item], /) -> None:
|
|
644
|
-
self._data: FrozenHoldList.Data[Item] = data
|
|
645
|
-
|
|
646
|
-
|
|
647
571
|
class DataList[Item](
|
|
648
572
|
BaseDataList[Item],
|
|
649
573
|
abc.MutableSequence[Item],
|
|
@@ -710,20 +634,3 @@ class DataList[Item](
|
|
|
710
634
|
data = self.__fget__()
|
|
711
635
|
data.sort(key=key, reverse=reverse)
|
|
712
636
|
self.__fset__(data)
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
class HoldList[Item](
|
|
716
|
-
DataList[Item],
|
|
717
|
-
BaseHoldCollection[Item],
|
|
718
|
-
):
|
|
719
|
-
"""Provide usable mutable list-like with slots."""
|
|
720
|
-
|
|
721
|
-
__slots__ = ()
|
|
722
|
-
|
|
723
|
-
@setdoc.basic
|
|
724
|
-
def __fget__(self: Self) -> HoldList.Data[Item]:
|
|
725
|
-
return list(self._data)
|
|
726
|
-
|
|
727
|
-
@setdoc.basic
|
|
728
|
-
def __fset__(self: Self, value: HoldList.Data[Item], /) -> None:
|
|
729
|
-
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()
|
|
@@ -1,8 +1,44 @@
|
|
|
1
|
-
__all__: list[str] = [
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestCopy",
|
|
3
|
+
"TestDataAttribute",
|
|
4
|
+
"TestHoldList",
|
|
5
|
+
"TestMutableBehavior",
|
|
6
|
+
]
|
|
2
7
|
import unittest
|
|
3
8
|
from typing import Any, Self
|
|
4
9
|
|
|
5
|
-
from datahold import
|
|
10
|
+
from datahold import BaseHoldObject, DataList
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HoldList[Item](
|
|
14
|
+
BaseHoldObject[DataList.Data[Item]],
|
|
15
|
+
DataList[Item],
|
|
16
|
+
):
|
|
17
|
+
"""Provide usable mutable list-like with slots."""
|
|
18
|
+
|
|
19
|
+
__slots__ = ()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TestCopy(unittest.TestCase):
|
|
23
|
+
|
|
24
|
+
def test_list_copy(self: Self) -> None:
|
|
25
|
+
lst: HoldList[Any]
|
|
26
|
+
lst_copy: HoldList[Any]
|
|
27
|
+
lst = HoldList([[1], [2]])
|
|
28
|
+
lst_copy = lst.copy()
|
|
29
|
+
self.assertIsInstance(lst_copy, type(lst))
|
|
30
|
+
self.assertIsNot(lst_copy, lst)
|
|
31
|
+
self.assertEqual(list(lst_copy), list(lst))
|
|
32
|
+
|
|
33
|
+
lst[0].append(99)
|
|
34
|
+
self.assertEqual(lst_copy[0], [1, 99])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TestDataAttribute(unittest.TestCase):
|
|
38
|
+
def test_list_data_is_tuple(self: Self) -> None:
|
|
39
|
+
m: HoldList[Any]
|
|
40
|
+
m = HoldList([1, 2, 3])
|
|
41
|
+
self.assertIsInstance(m.__fget__(), list)
|
|
6
42
|
|
|
7
43
|
|
|
8
44
|
class TestHoldList(unittest.TestCase):
|
|
@@ -212,5 +248,15 @@ class TestHoldList(unittest.TestCase):
|
|
|
212
248
|
)
|
|
213
249
|
|
|
214
250
|
|
|
251
|
+
class TestMutableBehavior(unittest.TestCase):
|
|
252
|
+
|
|
253
|
+
def test_hold_list_mutates_and_syncs_data(self: Self) -> None:
|
|
254
|
+
x: HoldList[Any]
|
|
255
|
+
x = HoldList([1, 2])
|
|
256
|
+
x.append(3)
|
|
257
|
+
self.assertEqual(list(x), [1, 2, 3])
|
|
258
|
+
self.assertEqual(x.__fget__(), [1, 2, 3])
|
|
259
|
+
|
|
260
|
+
|
|
215
261
|
if __name__ == "__main__":
|
|
216
262
|
unittest.main()
|
|
@@ -1,7 +1,45 @@
|
|
|
1
|
+
__all__: list[str] = [
|
|
2
|
+
"TestCopy",
|
|
3
|
+
"TestDataAttribute",
|
|
4
|
+
"TestHoldSet",
|
|
5
|
+
"TestMutableBehavior",
|
|
6
|
+
]
|
|
1
7
|
import unittest
|
|
8
|
+
from collections import abc
|
|
2
9
|
from typing import Any, Self
|
|
3
10
|
|
|
4
|
-
from datahold import
|
|
11
|
+
from datahold import BaseHoldObject, DataSet
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HoldSet[Item: abc.Hashable](
|
|
15
|
+
BaseHoldObject[DataSet.Data[Item]],
|
|
16
|
+
DataSet[Item],
|
|
17
|
+
):
|
|
18
|
+
"""Provide usable mutable set-like with slots."""
|
|
19
|
+
|
|
20
|
+
__slots__ = ()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestCopy(unittest.TestCase):
|
|
24
|
+
|
|
25
|
+
def test_set_copy(self: Self) -> None:
|
|
26
|
+
s: HoldSet[Any]
|
|
27
|
+
s_copy: HoldSet[Any]
|
|
28
|
+
s = HoldSet({1, 2, 3})
|
|
29
|
+
s_copy = s.copy()
|
|
30
|
+
self.assertIsInstance(s_copy, type(s))
|
|
31
|
+
self.assertIsNot(s_copy, s)
|
|
32
|
+
self.assertEqual(set(s_copy), set(s))
|
|
33
|
+
|
|
34
|
+
s.add(4)
|
|
35
|
+
self.assertNotIn(4, s_copy)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestDataAttribute(unittest.TestCase):
|
|
39
|
+
def test_set_data_is_frozenset(self: Self) -> None:
|
|
40
|
+
m: HoldSet[Any]
|
|
41
|
+
m = HoldSet({1, 2, 3})
|
|
42
|
+
self.assertIsInstance(m.__fget__(), set)
|
|
5
43
|
|
|
6
44
|
|
|
7
45
|
class TestHoldSet(unittest.TestCase):
|
|
@@ -234,5 +272,14 @@ class TestHoldSet(unittest.TestCase):
|
|
|
234
272
|
)
|
|
235
273
|
|
|
236
274
|
|
|
275
|
+
class TestMutableBehavior(unittest.TestCase):
|
|
276
|
+
def test_hold_set_mutates_and_syncs_data(self: Self) -> None:
|
|
277
|
+
s: HoldSet[Any]
|
|
278
|
+
s = HoldSet({1, 2})
|
|
279
|
+
s.add(3)
|
|
280
|
+
self.assertTrue(3 in s)
|
|
281
|
+
self.assertTrue(3 in s.__fget__())
|
|
282
|
+
|
|
283
|
+
|
|
237
284
|
if __name__ == "__main__":
|
|
238
285
|
unittest.main()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
__all__: list[str] = [
|
|
2
2
|
"TestAbstractness",
|
|
3
|
+
"TestAll",
|
|
3
4
|
"TestCollection",
|
|
4
5
|
"TestConstructor",
|
|
5
6
|
"TestData",
|
|
@@ -25,6 +26,10 @@ import datahold
|
|
|
25
26
|
class Lazy(enum.Enum):
|
|
26
27
|
lazy = None
|
|
27
28
|
|
|
29
|
+
@cached_property
|
|
30
|
+
def HOLD_TYPES(self: Self) -> list[str]:
|
|
31
|
+
return cast(list[str], self.varia["HOLD_TYPES"])
|
|
32
|
+
|
|
28
33
|
@cached_property
|
|
29
34
|
def METHODS(self: Self) -> tuple[str, ...]:
|
|
30
35
|
return cast(tuple[str, ...], self.test_object["METHODS"])
|
|
@@ -54,6 +59,10 @@ class Lazy(enum.Enum):
|
|
|
54
59
|
def types(self: Self) -> dict[str, dict[str, Any]]:
|
|
55
60
|
return cast(dict[str, dict[str, Any]], self.data["types"])
|
|
56
61
|
|
|
62
|
+
@cached_property
|
|
63
|
+
def varia(self: Self) -> dict[str, Any]:
|
|
64
|
+
return cast(dict[str, Any], self.data["varia"])
|
|
65
|
+
|
|
57
66
|
|
|
58
67
|
class TestAbstractness(unittest.TestCase):
|
|
59
68
|
|
|
@@ -69,6 +78,18 @@ class TestAbstractness(unittest.TestCase):
|
|
|
69
78
|
self.go_types(typename, **kwargs)
|
|
70
79
|
|
|
71
80
|
|
|
81
|
+
class TestAll(unittest.TestCase):
|
|
82
|
+
def test_all(self: Self) -> None:
|
|
83
|
+
self.assertLessEqual(
|
|
84
|
+
set(datahold.__all__),
|
|
85
|
+
Lazy.lazy.types.keys() | Lazy.lazy.HOLD_TYPES,
|
|
86
|
+
)
|
|
87
|
+
self.assertListEqual(
|
|
88
|
+
datahold.__all__,
|
|
89
|
+
list(sorted(datahold.__all__)),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
72
93
|
class TestCollection(unittest.TestCase):
|
|
73
94
|
def _test_cls(self: Self, cls: type) -> None:
|
|
74
95
|
method: Any
|
|
@@ -182,10 +203,8 @@ class TestDirData(unittest.TestCase):
|
|
|
182
203
|
def go_init(self: Self, x: str, /) -> None:
|
|
183
204
|
cls: Any
|
|
184
205
|
cls = getattr(datahold, x)
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
"Data" in cls.__dict__ or not ins.isabstract(cls),
|
|
188
|
-
)
|
|
206
|
+
if "__fget__" in cls.__dict__:
|
|
207
|
+
self.assertTrue("Data" in cls.__dict__ or not ins.isabstract(cls))
|
|
189
208
|
if "__fset__" in cls.__dict__:
|
|
190
209
|
self.assertIn("__fget__", cls.__dict__)
|
|
191
210
|
self.assertEqual(
|
|
@@ -48,7 +48,12 @@ BaseDataCollection = true
|
|
|
48
48
|
Hashable = false
|
|
49
49
|
|
|
50
50
|
[types.BaseDataSequence]
|
|
51
|
-
dirData = [
|
|
51
|
+
dirData = [
|
|
52
|
+
"__contains__",
|
|
53
|
+
"__getitem__",
|
|
54
|
+
"__iter__",
|
|
55
|
+
"__len__",
|
|
56
|
+
]
|
|
52
57
|
|
|
53
58
|
[types.BaseDataSequence.parents]
|
|
54
59
|
BaseDataCollection = true
|
|
@@ -58,16 +63,10 @@ Hashable = true
|
|
|
58
63
|
dirData = []
|
|
59
64
|
|
|
60
65
|
[types.BaseDataSet.parents]
|
|
66
|
+
BaseDataAbstractSet = true
|
|
61
67
|
Hashable = false
|
|
62
68
|
Set = true
|
|
63
69
|
|
|
64
|
-
[types.BaseHoldCollection]
|
|
65
|
-
dirData = []
|
|
66
|
-
|
|
67
|
-
[types.BaseHoldCollection.parents]
|
|
68
|
-
BaseDataCollection = true
|
|
69
|
-
Hashable = true
|
|
70
|
-
|
|
71
70
|
[types.DataDict]
|
|
72
71
|
Data = "frozendict"
|
|
73
72
|
dirData = []
|
|
@@ -79,6 +78,7 @@ valid = false
|
|
|
79
78
|
|
|
80
79
|
[types.DataDict.parents]
|
|
81
80
|
BaseDataDict = true
|
|
81
|
+
BaseDataMapping = true
|
|
82
82
|
MutableMapping = true
|
|
83
83
|
|
|
84
84
|
[types.DataList]
|
|
@@ -115,6 +115,8 @@ dirData = []
|
|
|
115
115
|
isabstract = true
|
|
116
116
|
|
|
117
117
|
[types.FrozenDataDict.parents]
|
|
118
|
+
BaseDataDict = true
|
|
119
|
+
FrozenDataMapping = true
|
|
118
120
|
|
|
119
121
|
[types.FrozenDataList]
|
|
120
122
|
Data = "tuple"
|
|
@@ -123,6 +125,13 @@ isabstract = true
|
|
|
123
125
|
|
|
124
126
|
[types.FrozenDataList.parents]
|
|
125
127
|
|
|
128
|
+
[types.FrozenDataMapping]
|
|
129
|
+
dirData = []
|
|
130
|
+
|
|
131
|
+
[types.FrozenDataMapping.parents]
|
|
132
|
+
BaseDataMapping = true
|
|
133
|
+
Hashable = true
|
|
134
|
+
|
|
126
135
|
[types.FrozenDataSet]
|
|
127
136
|
Data = "frozenset"
|
|
128
137
|
dirData = []
|
|
@@ -130,130 +139,7 @@ isabstract = true
|
|
|
130
139
|
|
|
131
140
|
[types.FrozenDataSet.parents]
|
|
132
141
|
|
|
133
|
-
[types.FrozenHoldDict]
|
|
134
|
-
dirData = []
|
|
135
|
-
|
|
136
|
-
[types.FrozenHoldDict.constructor.concrete]
|
|
137
|
-
args = [
|
|
138
|
-
{ a = 1 },
|
|
139
|
-
]
|
|
140
|
-
|
|
141
|
-
[types.FrozenHoldDict.constructor.concrete-1.kwargs]
|
|
142
|
-
a = 1
|
|
143
|
-
|
|
144
|
-
[types.FrozenHoldDict.parents]
|
|
145
|
-
Mapping = true
|
|
146
|
-
MutableMapping = false
|
|
147
|
-
|
|
148
|
-
[types.FrozenHoldList]
|
|
149
|
-
dirData = []
|
|
150
|
-
|
|
151
|
-
[types.FrozenHoldList.constructor.concrete]
|
|
152
|
-
args = [
|
|
153
|
-
[
|
|
154
|
-
1,
|
|
155
|
-
2,
|
|
156
|
-
],
|
|
157
|
-
]
|
|
158
|
-
repr = "FrozenHoldList([1, 2])"
|
|
159
|
-
|
|
160
|
-
[types.FrozenHoldList.parents]
|
|
161
|
-
FrozenDataList = true
|
|
162
|
-
MutableSequence = false
|
|
163
|
-
Sequence = true
|
|
164
|
-
|
|
165
|
-
[types.FrozenHoldSet]
|
|
166
|
-
dirData = []
|
|
167
|
-
|
|
168
|
-
[types.FrozenHoldSet.constructor.concrete]
|
|
169
|
-
args = [
|
|
170
|
-
[
|
|
171
|
-
1,
|
|
172
|
-
2,
|
|
173
|
-
],
|
|
174
|
-
]
|
|
175
|
-
valid = true
|
|
176
|
-
|
|
177
|
-
[types.FrozenHoldSet.parents]
|
|
178
|
-
MutableSet = false
|
|
179
|
-
Set = true
|
|
180
|
-
|
|
181
|
-
[types.HoldDict]
|
|
182
|
-
Data = "frozendict"
|
|
183
|
-
dirData = []
|
|
184
|
-
n_type_params = 2
|
|
185
|
-
|
|
186
|
-
[types.HoldDict.constructor.concrete.kwargs]
|
|
187
|
-
a = 1
|
|
188
|
-
|
|
189
|
-
[types.HoldDict.constructor.empty]
|
|
190
|
-
args = []
|
|
191
|
-
|
|
192
|
-
[types.HoldDict.parents]
|
|
193
|
-
BaseDataDict = true
|
|
194
|
-
DataDict = true
|
|
195
|
-
Mapping = true
|
|
196
|
-
MutableMapping = true
|
|
197
|
-
|
|
198
|
-
[types.HoldList]
|
|
199
|
-
Data = "tuple"
|
|
200
|
-
dirData = []
|
|
201
|
-
n_type_params = 1
|
|
202
|
-
|
|
203
|
-
[types.HoldList.constructor.empty]
|
|
204
|
-
args = []
|
|
205
|
-
|
|
206
|
-
[types.HoldList.constructor.concrete]
|
|
207
|
-
args = [
|
|
208
|
-
[
|
|
209
|
-
1,
|
|
210
|
-
2,
|
|
211
|
-
],
|
|
212
|
-
]
|
|
213
|
-
|
|
214
|
-
[types.HoldList.constructor.protocols]
|
|
215
|
-
args = [
|
|
216
|
-
[
|
|
217
|
-
1,
|
|
218
|
-
2,
|
|
219
|
-
3,
|
|
220
|
-
],
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
[types.HoldList.parents]
|
|
224
|
-
DataList = true
|
|
225
|
-
MutableSequence = true
|
|
226
|
-
Sequence = true
|
|
227
|
-
|
|
228
|
-
[types.HoldSet]
|
|
229
|
-
Date = "frozenset"
|
|
230
|
-
dirData = []
|
|
231
|
-
n_type_params = 1
|
|
232
|
-
|
|
233
|
-
[types.HoldSet.constructor.empty]
|
|
234
|
-
args = []
|
|
235
|
-
|
|
236
|
-
[types.HoldSet.constructor.concrete]
|
|
237
|
-
args = [
|
|
238
|
-
[
|
|
239
|
-
1,
|
|
240
|
-
2,
|
|
241
|
-
],
|
|
242
|
-
]
|
|
243
|
-
|
|
244
|
-
[types.HoldSet.constructor.protocols]
|
|
245
|
-
args = [
|
|
246
|
-
[
|
|
247
|
-
1,
|
|
248
|
-
2,
|
|
249
|
-
3,
|
|
250
|
-
],
|
|
251
|
-
]
|
|
252
|
-
|
|
253
|
-
[types.HoldSet.parents]
|
|
254
|
-
BaseDataSet = true
|
|
255
|
-
DataSet = true
|
|
256
|
-
MutableSet = true
|
|
257
|
-
Set = true
|
|
258
|
-
|
|
259
142
|
[varia]
|
|
143
|
+
HOLD_TYPES = [
|
|
144
|
+
"BaseHoldObject",
|
|
145
|
+
]
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
__all__: list[str] = ["TestCopy"]
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
from typing import Any, Self, cast
|
|
5
|
-
|
|
6
|
-
from datahold import (
|
|
7
|
-
FrozenHoldDict,
|
|
8
|
-
FrozenHoldList,
|
|
9
|
-
FrozenHoldSet,
|
|
10
|
-
HoldDict,
|
|
11
|
-
HoldList,
|
|
12
|
-
HoldSet,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TestCopy(unittest.TestCase):
|
|
17
|
-
|
|
18
|
-
def test_frozen_have_no_copy_2(self: Self) -> None:
|
|
19
|
-
"""
|
|
20
|
-
Frozen classes should not define their own copy method.
|
|
21
|
-
(If a parent class or wrapped object exposes one, we ignore that.)
|
|
22
|
-
"""
|
|
23
|
-
args: Any
|
|
24
|
-
cls: Any
|
|
25
|
-
copy_obj: Any
|
|
26
|
-
obj: Any
|
|
27
|
-
for cls, args in (
|
|
28
|
-
(FrozenHoldDict, ({"a": 1},)),
|
|
29
|
-
(FrozenHoldList, ([1, 2],)),
|
|
30
|
-
(FrozenHoldSet, ({1, 2},)),
|
|
31
|
-
):
|
|
32
|
-
# They must not *define* copy themselves
|
|
33
|
-
self.assertNotIn("copy", cls.__dict__)
|
|
34
|
-
|
|
35
|
-
# Optional: if they *do* expose copy on the instance, it should not
|
|
36
|
-
# create a mutable variant; you can drop this if you prefer.
|
|
37
|
-
obj = cls(*args)
|
|
38
|
-
if hasattr(obj, "copy"):
|
|
39
|
-
copy_obj = obj.copy()
|
|
40
|
-
self.assertIsInstance(copy_obj, cls)
|
|
41
|
-
|
|
42
|
-
def test_mutable_copy_returns_same_type_and_is_shallow(self: Self) -> None:
|
|
43
|
-
d: HoldDict[str, dict[str, int]]
|
|
44
|
-
d_copy: HoldDict[str, dict[str, int]]
|
|
45
|
-
d = HoldDict({"a": {"x": 1}})
|
|
46
|
-
d_copy = d.copy()
|
|
47
|
-
self.assertIsInstance(d_copy, type(d))
|
|
48
|
-
self.assertIsNot(d_copy, d)
|
|
49
|
-
self.assertEqual(dict(d_copy), dict(d))
|
|
50
|
-
|
|
51
|
-
# shallow: inner object is shared
|
|
52
|
-
cast(dict[str, int], d["a"])["x"] = 2
|
|
53
|
-
self.assertEqual(cast(dict[str, int], d_copy["a"])["x"], 2)
|
|
54
|
-
|
|
55
|
-
def test_list_copy(self: Self) -> None:
|
|
56
|
-
lst: HoldList[Any]
|
|
57
|
-
lst_copy: HoldList[Any]
|
|
58
|
-
lst = HoldList([[1], [2]])
|
|
59
|
-
lst_copy = lst.copy()
|
|
60
|
-
self.assertIsInstance(lst_copy, type(lst))
|
|
61
|
-
self.assertIsNot(lst_copy, lst)
|
|
62
|
-
self.assertEqual(list(lst_copy), list(lst))
|
|
63
|
-
|
|
64
|
-
lst[0].append(99)
|
|
65
|
-
self.assertEqual(lst_copy[0], [1, 99])
|
|
66
|
-
|
|
67
|
-
def test_set_copy(self: Self) -> None:
|
|
68
|
-
s: HoldSet[Any]
|
|
69
|
-
s_copy: HoldSet[Any]
|
|
70
|
-
s = HoldSet({1, 2, 3})
|
|
71
|
-
s_copy = s.copy()
|
|
72
|
-
self.assertIsInstance(s_copy, type(s))
|
|
73
|
-
self.assertIsNot(s_copy, s)
|
|
74
|
-
self.assertEqual(set(s_copy), set(s))
|
|
75
|
-
|
|
76
|
-
s.add(4)
|
|
77
|
-
self.assertNotIn(4, s_copy)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if __name__ == "__main__":
|
|
81
|
-
unittest.main()
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
__all__: list[str] = ["TestDataAttribute"]
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
from typing import Any, Self
|
|
5
|
-
|
|
6
|
-
from datahold import (
|
|
7
|
-
FrozenHoldDict,
|
|
8
|
-
FrozenHoldList,
|
|
9
|
-
FrozenHoldSet,
|
|
10
|
-
HoldDict,
|
|
11
|
-
HoldList,
|
|
12
|
-
HoldSet,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TestDataAttribute(unittest.TestCase):
|
|
17
|
-
def test_dict_data_is_immutable_mapping(self: Self) -> None:
|
|
18
|
-
f: FrozenHoldDict[Any, Any]
|
|
19
|
-
m: HoldDict[Any, Any]
|
|
20
|
-
obj: Any
|
|
21
|
-
f = FrozenHoldDict({"a": 1})
|
|
22
|
-
m = HoldDict({"a": 1})
|
|
23
|
-
for obj in (f, m):
|
|
24
|
-
self.assertIsInstance(obj.__fget__(), dict)
|
|
25
|
-
|
|
26
|
-
def test_list_data_is_tuple(self: Self) -> None:
|
|
27
|
-
f: FrozenHoldList[Any]
|
|
28
|
-
m: HoldList[Any]
|
|
29
|
-
o: Any
|
|
30
|
-
f = FrozenHoldList([1, 2, 3])
|
|
31
|
-
m = HoldList([1, 2, 3])
|
|
32
|
-
for o in (f, m):
|
|
33
|
-
self.assertIsInstance(o.__fget__(), list)
|
|
34
|
-
|
|
35
|
-
def test_set_data_is_frozenset(self: Self) -> None:
|
|
36
|
-
f: FrozenHoldSet[Any]
|
|
37
|
-
m: HoldSet[Any]
|
|
38
|
-
obj: Any
|
|
39
|
-
f = FrozenHoldSet({1, 2, 3})
|
|
40
|
-
m = HoldSet({1, 2, 3})
|
|
41
|
-
for obj in (f, m):
|
|
42
|
-
self.assertIsInstance(obj.__fget__(), set)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if __name__ == "__main__":
|
|
46
|
-
unittest.main()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
__all__: list[str] = ["TestFrozenMutability"]
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
from typing import Any, Self
|
|
5
|
-
|
|
6
|
-
from datahold import FrozenHoldDict, FrozenHoldList, FrozenHoldSet
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestFrozenMutability(unittest.TestCase):
|
|
10
|
-
def test_frozen_dict_cannot_mutate(self: Self) -> None:
|
|
11
|
-
f: Any
|
|
12
|
-
f = FrozenHoldDict({"a": 1})
|
|
13
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
14
|
-
f["b"] = 2
|
|
15
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
16
|
-
f.pop("a", None)
|
|
17
|
-
|
|
18
|
-
def test_frozen_list_cannot_mutate(self: Self) -> None:
|
|
19
|
-
f: Any
|
|
20
|
-
f = FrozenHoldList([1, 2, 3])
|
|
21
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
22
|
-
f.append(4)
|
|
23
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
24
|
-
f.pop()
|
|
25
|
-
|
|
26
|
-
def test_frozen_set_cannot_mutate(self: Self) -> None:
|
|
27
|
-
f: Any
|
|
28
|
-
f = FrozenHoldSet({1, 2, 3})
|
|
29
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
30
|
-
f.add(4)
|
|
31
|
-
with self.assertRaises((TypeError, AttributeError)):
|
|
32
|
-
f.remove(1)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if __name__ == "__main__":
|
|
36
|
-
unittest.main()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
__all__: list[str] = ["TestMutableBehavior"]
|
|
2
|
-
|
|
3
|
-
import unittest
|
|
4
|
-
from typing import Any, Self
|
|
5
|
-
|
|
6
|
-
from datahold import HoldDict, HoldList, HoldSet
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TestMutableBehavior(unittest.TestCase):
|
|
10
|
-
def test_hold_dict_mutates_and_syncs_data(self: Self) -> None:
|
|
11
|
-
x: HoldDict[Any, Any]
|
|
12
|
-
x = HoldDict({"a": 1})
|
|
13
|
-
x["b"] = 2
|
|
14
|
-
self.assertEqual(x["b"], 2)
|
|
15
|
-
self.assertEqual(x.__fget__()["b"], 2)
|
|
16
|
-
|
|
17
|
-
def test_hold_list_mutates_and_syncs_data(self: Self) -> None:
|
|
18
|
-
x: HoldList[Any]
|
|
19
|
-
x = HoldList([1, 2])
|
|
20
|
-
x.append(3)
|
|
21
|
-
self.assertEqual(list(x), [1, 2, 3])
|
|
22
|
-
self.assertEqual(x.__fget__(), [1, 2, 3])
|
|
23
|
-
|
|
24
|
-
def test_hold_set_mutates_and_syncs_data(self: Self) -> None:
|
|
25
|
-
s: HoldSet[Any]
|
|
26
|
-
s = HoldSet({1, 2})
|
|
27
|
-
s.add(3)
|
|
28
|
-
self.assertTrue(3 in s)
|
|
29
|
-
self.assertTrue(3 in s.__fget__())
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if __name__ == "__main__":
|
|
33
|
-
unittest.main()
|
|
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
|