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.
Files changed (34) hide show
  1. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/PKG-INFO +1 -1
  2. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/pyproject.toml +1 -1
  3. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold/__init__.py +59 -152
  4. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/PKG-INFO +1 -1
  5. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/SOURCES.txt +0 -4
  6. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldDict.py +56 -2
  7. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldList.py +55 -2
  8. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_FrozenHoldSet.py +60 -3
  9. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldDict.py +49 -3
  10. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldList.py +48 -2
  11. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_HoldSet.py +48 -1
  12. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/test_testdata.py +23 -4
  13. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/tests/testdata.toml +20 -134
  14. datahold-4.0.0.dev6/tests/test_datahold_TestCopy.py +0 -81
  15. datahold-4.0.0.dev6/tests/test_datahold_TestDataAttribute.py +0 -46
  16. datahold-4.0.0.dev6/tests/test_datahold_TestFrozenMutability.py +0 -36
  17. datahold-4.0.0.dev6/tests/test_datahold_TestMutableBehavior.py +0 -33
  18. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/LICENSE.txt +0 -0
  19. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/MANIFEST.in +0 -0
  20. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/README.rst +0 -0
  21. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v1.1.rst +0 -0
  22. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v1.2.rst +0 -0
  23. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.0.rst +0 -0
  24. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.1.rst +0 -0
  25. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.2.rst +0 -0
  26. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v2.3.rst +0 -0
  27. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v3.0.rst +0 -0
  28. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/docs/v4.0.rst +0 -0
  29. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/run_tests.py +0 -0
  30. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/setup.cfg +0 -0
  31. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold/py.typed +0 -0
  32. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/dependency_links.txt +0 -0
  33. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/requires.txt +0 -0
  34. {datahold-4.0.0.dev6 → datahold-4.0.0.dev8}/src/datahold.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev6
3
+ Version: 4.0.0.dev8
4
4
  Summary: This project wraps common mutable datastructures for inheritance with modification.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -34,7 +34,7 @@ license-files = [
34
34
  name = "datahold"
35
35
  readme = "README.rst"
36
36
  requires-python = ">=3.12"
37
- version = "4.0.0.dev6"
37
+ version = "4.0.0.dev8"
38
38
 
39
39
  [project.urls]
40
40
  Download = "https://pypi.org/project/datahold/#files"
@@ -10,19 +10,14 @@ __all__: list[str] = [
10
10
  "BaseDataMapping",
11
11
  "BaseDataSequence",
12
12
  "BaseDataSet",
13
- "BaseHoldCollection",
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
- self.__fset__(self.__fget__() | {item})
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
- data: set[Item]
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 = set(self.__fget__())
233
+ data = self.__fget__()
240
234
  data.discard(item)
241
- self.__fset__(set(data))
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
- data: set[Item]
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
- data: set[Item]
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
- pass
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
- @abstractmethod
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 __fget__(self: Self) -> Data[Key, Value]: ...
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
- abc.Hashable,
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datahold
3
- Version: 4.0.0.dev6
3
+ Version: 4.0.0.dev8
4
4
  Summary: This project wraps common mutable datastructures for inheritance with modification.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -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] = ["TestFrozenHoldDict"]
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 FrozenHoldDict
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] = ["TestFrozenHoldLists"]
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 FrozenHoldList
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] = ["TestFrozenHoldSet"]
1
+ __all__: list[str] = [
2
+ "TestCopy",
3
+ "TestDataAttribute",
4
+ "TestFrozenHoldSet",
5
+ "TestFrozenMutability",
6
+ ]
2
7
  import unittest
3
- from typing import Self
8
+ from collections import abc
9
+ from typing import Any, Self
4
10
 
5
- from datahold import FrozenHoldSet
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] = ["TestHoldDict"]
1
+ __all__: list[str] = [
2
+ "TestDataAttribute",
3
+ "TestHoldDict",
4
+ "TestMutableBehavior",
5
+ "TestCopy",
6
+ ]
2
7
  import unittest
3
- from typing import Any, Self
8
+ from collections import abc
9
+ from typing import Any, Self, cast
4
10
 
5
- from datahold import HoldDict
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] = ["TestHoldList"]
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 HoldList
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 HoldSet
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
- self.assertEqual(
186
- "__fget__" in cls.__dict__,
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