tensorneko-util 0.3.18__tar.gz → 0.3.19__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 (129) hide show
  1. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/PKG-INFO +1 -1
  2. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/image/image_reader.py +4 -1
  3. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/pickle/pickle_reader.py +3 -3
  4. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/__init__.py +3 -1
  5. tensorneko_util-0.3.19/src/tensorneko_util/util/multi_layer_indexer.py +113 -0
  6. tensorneko_util-0.3.19/src/tensorneko_util/version.txt +1 -0
  7. tensorneko_util-0.3.18/src/tensorneko_util/visualization/watcher/web/dist/assets/index.3222e532.js → tensorneko_util-0.3.19/src/tensorneko_util/visualization/watcher/web/dist/assets/index.901ba3f5.js +7 -7
  8. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/web/dist/index.html +1 -1
  9. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util.egg-info/PKG-INFO +1 -1
  10. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util.egg-info/SOURCES.txt +2 -1
  11. tensorneko_util-0.3.18/src/tensorneko_util/version.txt +0 -1
  12. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/README.md +0 -0
  13. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/setup.cfg +0 -0
  14. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/setup.py +0 -0
  15. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/setup_util.py +0 -0
  16. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/__init__.py +0 -0
  17. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/_rich.py +0 -0
  18. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/__init__.py +0 -0
  19. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/audio_lib.py +0 -0
  20. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/blocking.py +0 -0
  21. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/parallel.py +0 -0
  22. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/tqdm.py +0 -0
  23. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/backend/visual_lib.py +0 -0
  24. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/debug/__init__.py +0 -0
  25. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/debug/logger.py +0 -0
  26. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/debug/parser.py +0 -0
  27. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/__init__.py +0 -0
  28. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/_default_backends.py +0 -0
  29. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/_path_conversion.py +0 -0
  30. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/audio/__init__.py +0 -0
  31. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/audio/audio_data.py +0 -0
  32. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/audio/audio_reader.py +0 -0
  33. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/audio/audio_writer.py +0 -0
  34. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/hdf5/__init__.py +0 -0
  35. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/hdf5/hdf5_reader.py +0 -0
  36. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/hdf5/hdf5_writer.py +0 -0
  37. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/image/__init__.py +0 -0
  38. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/image/image_writer.py +0 -0
  39. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/json/__init__.py +0 -0
  40. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/json/json_data.py +0 -0
  41. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/json/json_reader.py +0 -0
  42. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/json/json_writer.py +0 -0
  43. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/matlab/__init__.py +0 -0
  44. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/matlab/mat_reader.py +0 -0
  45. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/matlab/mat_writer.py +0 -0
  46. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/npy/__init__.py +0 -0
  47. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/npy/npy_reader.py +0 -0
  48. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/npy/npy_writer.py +0 -0
  49. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/pickle/__init__.py +0 -0
  50. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/pickle/pickle_writer.py +0 -0
  51. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/reader.py +0 -0
  52. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/text/__init__.py +0 -0
  53. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/text/text_reader.py +0 -0
  54. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/text/text_writer.py +0 -0
  55. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/toml/__init__.py +0 -0
  56. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/toml/toml_reader.py +0 -0
  57. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/toml/toml_writer.py +0 -0
  58. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/video/__init__.py +0 -0
  59. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/video/video_data.py +0 -0
  60. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/video/video_reader.py +0 -0
  61. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/video/video_writer.py +0 -0
  62. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/writer.py +0 -0
  63. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/yaml/__init__.py +0 -0
  64. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/yaml/yaml_reader.py +0 -0
  65. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/io/yaml/yaml_writer.py +0 -0
  66. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/msg/__init__.py +0 -0
  67. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/msg/gotify.py +0 -0
  68. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/notebook/__init__.py +0 -0
  69. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/notebook/animation.py +0 -0
  70. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/notebook/display.py +0 -0
  71. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/__init__.py +0 -0
  72. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/crop.py +0 -0
  73. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/__init__.py +0 -0
  74. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/_utils.py +0 -0
  75. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/abstract_face_detector.py +0 -0
  76. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/anime_face_detector.py +0 -0
  77. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/facexzoo_face_detector.py +0 -0
  78. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/face_detector/opencv_face_detector.py +0 -0
  79. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/ffmpeg.py +0 -0
  80. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/image.py +0 -0
  81. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/preprocess/video.py +0 -0
  82. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/average_meter.py +0 -0
  83. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/bimap.py +0 -0
  84. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/dispatched_misc.py +0 -0
  85. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/dispatcher.py +0 -0
  86. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/downloader.py +0 -0
  87. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/eventbus/__init__.py +0 -0
  88. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/eventbus/bus.py +0 -0
  89. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/eventbus/decorator.py +0 -0
  90. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/eventbus/event.py +0 -0
  91. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/__init__.py +0 -0
  92. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/args.py +0 -0
  93. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/array/__init__.py +0 -0
  94. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/array/abstract_seq.py +0 -0
  95. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/array/seq.py +0 -0
  96. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/array/stream.py +0 -0
  97. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/func.py +0 -0
  98. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/monad/__init__.py +0 -0
  99. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/monad/eval.py +0 -0
  100. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/monad/monad.py +0 -0
  101. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/monad/option.py +0 -0
  102. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/fp/underscore.py +0 -0
  103. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/misc.py +0 -0
  104. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/ref.py +0 -0
  105. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/registry.py +0 -0
  106. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/server.py +0 -0
  107. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/singleton.py +0 -0
  108. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/timer.py +0 -0
  109. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/type.py +0 -0
  110. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/util/window_merger.py +0 -0
  111. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/__init__.py +0 -0
  112. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/color.py +0 -0
  113. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/image_browser/__init__.py +0 -0
  114. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/image_browser/server.py +0 -0
  115. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/image_browser/web/index.html +0 -0
  116. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/matplotlib.py +0 -0
  117. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/multi_plots.py +0 -0
  118. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/seaborn.py +0 -0
  119. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/tensorboard.py +0 -0
  120. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/__init__.py +0 -0
  121. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/component.py +0 -0
  122. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/server.py +0 -0
  123. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/view.py +0 -0
  124. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util/visualization/watcher/web/dist/assets/index.cf95019d.css +0 -0
  125. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util.egg-info/dependency_links.txt +0 -0
  126. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util.egg-info/requires.txt +0 -0
  127. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/src/tensorneko_util.egg-info/top_level.txt +0 -0
  128. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/test/test_library_info.py +0 -0
  129. {tensorneko_util-0.3.18 → tensorneko_util-0.3.19}/test/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tensorneko_util
3
- Version: 0.3.18
3
+ Version: 0.3.19
4
4
  Summary: The Utils for Library TensorNeko.
5
5
  Home-page: https://github.com/ControlNet/tensorneko
6
6
  Author: ControlNet
@@ -35,7 +35,10 @@ class ImageReader:
35
35
  if not VisualLib.opencv_available():
36
36
  raise ValueError("OpenCV is not installed.")
37
37
  import cv2
38
- img = cv2.imread(path).astype(np.float32) / 255.0
38
+ img = cv2.imread(path)
39
+ if img is None:
40
+ raise ValueError("Invalid image path: {}".format(path))
41
+ img = img.astype(np.float32) / 255.0
39
42
  img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
40
43
  elif backend == VisualLib.MATPLOTLIB:
41
44
  if not VisualLib.matplotlib_available():
@@ -1,5 +1,5 @@
1
1
  import pickle
2
- from typing import Union
2
+ from typing import Union, Any
3
3
  from pathlib import Path
4
4
 
5
5
  from .._path_conversion import _path2str
@@ -8,7 +8,7 @@ from .._path_conversion import _path2str
8
8
  class PickleReader:
9
9
 
10
10
  @classmethod
11
- def of(cls, path: Union[str, Path]) -> None:
11
+ def of(cls, path: Union[str, Path]) -> Any:
12
12
  """
13
13
  Save the object to a file.
14
14
 
@@ -19,7 +19,7 @@ class PickleReader:
19
19
  with open(path, 'rb') as f:
20
20
  return pickle.load(f)
21
21
 
22
- def __new__(cls, path: Union[str, Path]) -> None:
22
+ def __new__(cls, path: Union[str, Path]) -> Any:
23
23
  """Alias to :meth:`~tensorneko_util.io.pickle.PickleReader.of`."""
24
24
  path = _path2str(path)
25
25
  return cls.of(path)
@@ -12,6 +12,7 @@ from .eventbus import Event, EventBus, EventHandler, subscribe
12
12
  from .singleton import Singleton
13
13
  from .downloader import download_file, download_file_thread, download_files_thread
14
14
  from .window_merger import WindowMerger
15
+ from .multi_layer_indexer import MultiLayerIndexer
15
16
 
16
17
  tensorneko_util_path = get_tensorneko_util_path()
17
18
 
@@ -56,5 +57,6 @@ __all__ = [
56
57
  "download_file_thread",
57
58
  "download_files_thread",
58
59
  "WindowMerger",
59
- "Registry"
60
+ "Registry",
61
+ "MultiLayerIndexer"
60
62
  ]
@@ -0,0 +1,113 @@
1
+ from typing import Union, List
2
+
3
+ T_Counts = Union[int, List["Counts"]]
4
+
5
+
6
+ class MultiLayerIndexer:
7
+ """
8
+ Multi-layer indexer for hierarchical indexing structures.
9
+
10
+ Args:
11
+ counts (``int`` | ``List[int | List]``): A nested structure representing the counts at each layer.
12
+ It can be an integer or a list of counts (which can themselves be lists).
13
+
14
+ Examples::
15
+
16
+ # Test Case 1
17
+ counts = [5, 10, 15]
18
+ indexer = MultiLayerIndexer(counts)
19
+ print(len(indexer), indexer(12)) # Output: 30 [1, 7]
20
+
21
+ # Test Case 2
22
+ counts = [[3, 2], 4, [1, 1, 1]]
23
+ indexer = MultiLayerIndexer(counts)
24
+ print(len(indexer), indexer(8)) # Output: 12 [1, 3]
25
+
26
+ # Test Case 3 (Error Handling)
27
+ try:
28
+ counts = [2, [3, [4, 5]]]
29
+ indexer = MultiLayerIndexer(counts)
30
+ print(len(indexer), indexer(20)) # Should raise IndexError
31
+ except IndexError as e:
32
+ print(e) # Output: Index out of bounds
33
+
34
+ """
35
+
36
+ def __init__(self, counts: T_Counts):
37
+ self._counts = counts
38
+ self._total = self._total_counts(counts)
39
+
40
+ def __call__(self, index: int) -> List[int]:
41
+ """
42
+ Given a global index, return the indices at each layer.
43
+
44
+ Args:
45
+ index (``int``): The global index.
46
+
47
+ Returns:
48
+ ``List[int]``: A list of indices at each layer corresponding to the global index.
49
+
50
+ Raises:
51
+ ``IndexError``: If the provided global index is out of bounds.
52
+ """
53
+ return self._multi_layer_index(self._counts, index)
54
+
55
+ @classmethod
56
+ def _multi_layer_index(cls, counts: T_Counts, index: int) -> List[int]:
57
+ """
58
+ A recursive helper function to compute the indices at each layer.
59
+
60
+ Args:
61
+ counts (``int`` | ``List[int | List]``): The counts at the current layer.
62
+ index (``int``): The index at the current layer.
63
+
64
+ Returns:
65
+ ``list``: A list of indices at each layer.
66
+
67
+ Raises:
68
+ ``IndexError``: If the provided index exceeds the available counts.
69
+ """
70
+ if isinstance(counts, int):
71
+ if index < counts:
72
+ return [index]
73
+ else:
74
+ raise IndexError("Index out of bounds")
75
+ elif isinstance(counts, list):
76
+ cumulative_counts = [0]
77
+ for c in counts:
78
+ total_c = cls._total_counts(c)
79
+ cumulative_counts.append(cumulative_counts[-1] + total_c)
80
+ for i in range(len(cumulative_counts) - 1):
81
+ if cumulative_counts[i] <= index < cumulative_counts[i + 1]:
82
+ sub_idx = index - cumulative_counts[i]
83
+ sub_indices = cls._multi_layer_index(counts[i], sub_idx)
84
+ return [i] + sub_indices
85
+ raise IndexError("Index out of bounds")
86
+ else:
87
+ raise ValueError("Invalid counts structure")
88
+
89
+ @classmethod
90
+ def _total_counts(cls, counts: T_Counts) -> int:
91
+ """
92
+ Compute the total counts of items under the given counts structure.
93
+
94
+ Args:
95
+ counts (``int`` | ``List[int | List]``): The counts structure.
96
+
97
+ Returns:
98
+ ``int``: The total count of items.
99
+
100
+ Raises:
101
+ ``ValueError``: If the counts structure is invalid.
102
+ """
103
+ if isinstance(counts, int):
104
+ if counts < 0:
105
+ raise ValueError("Counts cannot be negative")
106
+ return counts
107
+ elif isinstance(counts, list):
108
+ return sum(cls._total_counts(c) for c in counts)
109
+ else:
110
+ raise ValueError("Invalid counts structure")
111
+
112
+ def __len__(self) -> int:
113
+ return self._total