Skip to content

Commit 772a075

Browse files
committed
feat(internal[sparse_array]): Add SparseArray
1 parent 55d606b commit 772a075

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Sparse array for libtmux options and hooks."""
2+
3+
import typing as t
4+
5+
if t.TYPE_CHECKING:
6+
from typing_extensions import TypeAlias, TypeGuard
7+
8+
from libtmux.options import ExplodedComplexUntypedOptionsDict
9+
10+
11+
T = t.TypeVar("T")
12+
HookArray: "TypeAlias" = "t.Dict[str, SparseArray[str]]"
13+
14+
15+
def is_sparse_array_list(
16+
items: "ExplodedComplexUntypedOptionsDict",
17+
) -> "TypeGuard[HookArray]":
18+
return all(
19+
isinstance(
20+
v,
21+
SparseArray,
22+
)
23+
for k, v in items.items()
24+
)
25+
26+
27+
class SparseArray(dict[int, T], t.Generic[T]):
28+
"""Support non-sequential indexes while maintaining :class:`list`-like behavior.
29+
30+
A normal :class:`list` would raise :exc:`IndexError`.
31+
32+
There are no native sparse arrays in python that contain non-sequential indexes and
33+
maintain list-like behavior. This is useful for handling libtmux options and hooks:
34+
35+
``command-alias[1] split-pane=split-window`` to
36+
``{'command-alias[1]': {'split-pane=split-window'}}``
37+
38+
:class:`list` would lose indice info, and :class:`dict` would lose list-like
39+
behavior.
40+
"""
41+
42+
def add(self, index: int, value: T) -> None:
43+
self[index] = value
44+
45+
def append(self, value: T) -> None:
46+
index = max(self.keys()) + 1
47+
self[index] = value
48+
49+
def iter_values(self) -> t.Iterator[T]:
50+
for index in sorted(self.keys()):
51+
yield self[index]
52+
53+
def as_list(self) -> list[T]:
54+
return [self[index] for index in sorted(self.keys())]

0 commit comments

Comments
 (0)