Repository: Textualize/textual

Issue:
Sparklines do not work with collections.deque
```python
from collections import deque

from textual.app import App, ComposeResult
from textual.widgets import Sparkline

data = deque([1, 2, 2, 1, 1, 4, 3, 1, 1, 8, 8, 2])


class SparklineBasicApp(App[None]):
    def compose(self) -> ComposeResult:
        yield Sparkline(
            data,
            summary_function=max,
        )


app = SparklineBasicApp()
if __name__ == "__main__":
    app.run()
```
I was working on creating a scrolling Sparkline and ran into this problem. When using a deque as the `data` element for a Sparkline you receive a TypeError: sequence index must be integer, not 'slice' exception.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b6eb7ff0..449eb7581 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 - Fixed `TextArea.code_editor` missing recently added attributes https://github.com/Textualize/textual/pull/4172
+- Fixed `Sparkline` not working with data in a `deque` https://github.com/Textualize/textual/issues/3899
 
 ## [0.52.1] - 2024-02-20
 
diff --git a/src/textual/renderables/sparkline.py b/src/textual/renderables/sparkline.py
index 8b22aff02..4da25fa23 100644
--- a/src/textual/renderables/sparkline.py
+++ b/src/textual/renderables/sparkline.py
@@ -45,7 +45,7 @@ class Sparkline(Generic[T]):
         self.summary_function: SummaryFunction[T] = summary_function
 
     @classmethod
-    def _buckets(cls, data: Sequence[T], num_buckets: int) -> Iterable[Sequence[T]]:
+    def _buckets(cls, data: list[T], num_buckets: int) -> Iterable[Sequence[T]]:
         """Partition ``data`` into ``num_buckets`` buckets. For example, the data
         [1, 2, 3, 4] partitioned into 2 buckets is [[1, 2], [3, 4]].
 
@@ -76,7 +76,7 @@ class Sparkline(Generic[T]):
         minimum, maximum = min(self.data), max(self.data)
         extent = maximum - minimum or 1
 
-        buckets = tuple(self._buckets(self.data, num_buckets=width))
+        buckets = tuple(self._buckets(list(self.data), num_buckets=width))
 
         bucket_index = 0.0
         bars_rendered = 0


Test patch:
diff --git a/tests/renderables/test_sparkline.py b/tests/renderables/test_sparkline.py
index 12e53ea72..1478e00b3 100644
--- a/tests/renderables/test_sparkline.py
+++ b/tests/renderables/test_sparkline.py
@@ -1,3 +1,8 @@
+from collections import UserList, deque
+from typing import Sequence
+
+import pytest
+
 from tests.utilities.render import render
 from textual.renderables.sparkline import Sparkline
 
@@ -46,3 +51,24 @@ def test_sparkline_color_blend():
         render(Sparkline([1, 2, 3], width=3))
         == f"{GREEN}▁{STOP}{BLENDED}▄{STOP}{RED}█{STOP}"
     )
+
+
+@pytest.mark.parametrize(
+    "data",
+    [
+        (1, 2, 3),
+        [1, 2, 3],
+        bytearray((1, 2, 3)),
+        bytes((1, 2, 3)),
+        deque([1, 2, 3]),
+        range(1, 4),
+        UserList((1, 2, 3)),
+    ],
+)
+def test_sparkline_sequence_types(data: Sequence[int]):
+    """Sparkline should work with common Sequence types."""
+    assert issubclass(type(data), Sequence)
+    assert (
+        render(Sparkline(data, width=3))
+        == f"{GREEN}▁{STOP}{BLENDED}▄{STOP}{RED}█{STOP}"
+    )