datahold 4.0.0.dev6__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.dev6 → datahold-4.0.0.dev7}/PKG-INFO +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/pyproject.toml +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold/__init__.py +31 -127
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold.egg-info/PKG-INFO +1 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold.egg-info/SOURCES.txt +0 -4
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_FrozenHoldDict.py +56 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_FrozenHoldList.py +55 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_FrozenHoldSet.py +60 -3
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_HoldDict.py +49 -3
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_HoldList.py +48 -2
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_HoldSet.py +48 -1
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/test_testdata.py +23 -4
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/tests/testdata.toml +11 -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.dev7}/LICENSE.txt +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/MANIFEST.in +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/README.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v1.1.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v1.2.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v2.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v2.1.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v2.2.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v2.3.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v3.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/docs/v4.0.rst +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/run_tests.py +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/setup.cfg +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold/py.typed +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold.egg-info/dependency_links.txt +0 -0
- {datahold-4.0.0.dev6 → datahold-4.0.0.dev7}/src/datahold.egg-info/requires.txt +0 -0
- {datahold-4.0.0.dev6 → 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
|
|
|
@@ -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],
|
|
@@ -229,57 +222,40 @@ class DataSet[Item: abc.Hashable](
|
|
|
229
222
|
*others: abc.Iterable[abc.Hashable],
|
|
230
223
|
) -> None:
|
|
231
224
|
data: set[Item]
|
|
232
|
-
data =
|
|
225
|
+
data = self.__fget__()
|
|
233
226
|
data.difference_update(*others)
|
|
234
|
-
self.__fset__(
|
|
227
|
+
self.__fset__(data)
|
|
235
228
|
|
|
236
229
|
@setdoc.basic
|
|
237
230
|
def discard(self: Self, item: abc.Hashable, /) -> None:
|
|
238
231
|
data: set[Item]
|
|
239
|
-
data =
|
|
232
|
+
data = self.__fget__()
|
|
240
233
|
data.discard(item)
|
|
241
|
-
self.__fset__(
|
|
234
|
+
self.__fset__(data)
|
|
242
235
|
|
|
243
236
|
@setdoc.basic
|
|
244
237
|
def intersection_update(
|
|
245
238
|
self: Self, /, *others: abc.Iterable[abc.Hashable]
|
|
246
239
|
) -> None:
|
|
247
240
|
data: set[Item]
|
|
248
|
-
data =
|
|
241
|
+
data = self.__fget__()
|
|
249
242
|
data.intersection_update(*others)
|
|
250
|
-
self.__fset__(
|
|
243
|
+
self.__fset__(data)
|
|
251
244
|
|
|
252
245
|
@setdoc.basic
|
|
253
246
|
def symmetric_difference_update(
|
|
254
247
|
self: Self, other: abc.Iterable[Item], /
|
|
255
248
|
) -> None:
|
|
256
249
|
data: set[Item]
|
|
257
|
-
data =
|
|
250
|
+
data = self.__fget__()
|
|
258
251
|
data.symmetric_difference_update(other)
|
|
259
|
-
self.__fset__(
|
|
252
|
+
self.__fset__(data)
|
|
260
253
|
|
|
261
254
|
@setdoc.basic
|
|
262
255
|
def update(self: Self, /, *others: abc.Iterable[Item]) -> None:
|
|
263
256
|
self.__fset__(self.__fget__().union(*others))
|
|
264
257
|
|
|
265
258
|
|
|
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
259
|
### MAPPING ###
|
|
284
260
|
class BaseDataMapping[Key, Value](
|
|
285
261
|
BaseDataCollection[Key], abc.Mapping[Key, Value]
|
|
@@ -385,23 +361,6 @@ class FrozenDataDict[Key: abc.Hashable, Value](
|
|
|
385
361
|
return hash(frozendict(self.__fget__()))
|
|
386
362
|
|
|
387
363
|
|
|
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
|
-
|
|
405
364
|
class DataDict[Key: abc.Hashable, Value](
|
|
406
365
|
BaseDataDict[Key, Value],
|
|
407
366
|
abc.MutableMapping[Key | str, Optional[Value]],
|
|
@@ -444,27 +403,6 @@ class DataDict[Key: abc.Hashable, Value](
|
|
|
444
403
|
return type(self)(self)
|
|
445
404
|
|
|
446
405
|
|
|
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
406
|
### SEQUENCE ###
|
|
469
407
|
|
|
470
408
|
|
|
@@ -627,23 +565,6 @@ class FrozenDataList[Item](
|
|
|
627
565
|
return hash(tuple(self.__fget__()))
|
|
628
566
|
|
|
629
567
|
|
|
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
568
|
class DataList[Item](
|
|
648
569
|
BaseDataList[Item],
|
|
649
570
|
abc.MutableSequence[Item],
|
|
@@ -710,20 +631,3 @@ class DataList[Item](
|
|
|
710
631
|
data = self.__fget__()
|
|
711
632
|
data.sort(key=key, reverse=reverse)
|
|
712
633
|
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]
|
|
@@ -130,130 +130,7 @@ isabstract = true
|
|
|
130
130
|
|
|
131
131
|
[types.FrozenDataSet.parents]
|
|
132
132
|
|
|
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
133
|
[varia]
|
|
134
|
+
HOLD_TYPES = [
|
|
135
|
+
"BaseHoldObject",
|
|
136
|
+
]
|
|
@@ -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
|