Repository: matplotlib/matplotlib

Issue:
[Bug]:  'PillowWriter' object has no attribute '_frame' shoudln't be  '_frames'?
### Bug summary

It seems to be that `gif` animations cannot be generated anymore because of empty list for `ax_fig.lines[0].get_xdata()`.

Currently, I got the following error in the CI run.

```terminal
UNEXPECTED EXCEPTION: IndexError('list index out of range')
Traceback (most recent call last):
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 224, in saving
    yield self
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 1126, in save
    writer.grab_frame(**savefig_kwargs)
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 499, in grab_frame
    self._frame.append(im)
AttributeError: 'PillowWriter' object has no attribute '_frame'. Did you mean: '_frames'?
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/home/runner/.local/share/uv/python/cpython-3.10.16-linux-x86_64-gnu/lib/python3.10/doctest.py", line 1350, in __run
    exec(compile(example.source, filename, "single",
  File "<doctest umf.images.diagrams.ClassicPlot[30]>", line 1, in <module>
  File "/home/runner/work/useful-math-functions/useful-math-functions/umf/images/diagrams.py", line 335, in plot_save_gif
    anim.save(filename=fname, writer="imagemagick", dpi=settings.dpi, **kwargs)
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 1098, in save
    with (writer.saving(self._fig, filename, dpi),
  File "/home/runner/.local/share/uv/python/cpython-3.10.16-linux-x86_64-gnu/lib/python3.10/contextlib.py", line 153, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 226, in saving
    self.finish()
  File "/home/runner/work/useful-math-functions/useful-math-functions/.venv/lib/python3.10/site-packages/matplotlib/animation.py", line 506, in finish
    self._frames[0].save(
IndexError: list index out of range
/home/runner/work/useful-math-functions/useful-math-functions/umf/images/diagrams.py:73: UnexpectedException
```

See also:

https://github.com/matplotlib/matplotlib/blob/f39bf4d2585746fce3ba39decba62693c82f194e/lib/matplotlib/animation.py#L499C21-L499C35

### Code for reproduction

```Python
https://github.com/Anselmoo/useful-math-functions/pull/84
```

### Actual outcome

```shell
AttributeError: 'PillowWriter' object has no attribute '_frame'. Did you mean: '_frames'?
```

### Expected outcome

Working like previously https://github.com/Anselmoo/useful-math-functions/pull/71

So, I have tested both backend `poetry` and `uv`, but it seems both are not working.

### Additional information

If `_frame` is changed to `_frames` in local `.venv`, it is working fine.

```toml
dependencies = [
    "numpy>=2.0.0,<3",
    "pydantic>=2.0.0,<3",
    "scipy>=1.11.2,<2",
]

[project.optional-dependencies]
matplotlib = [
    "matplotlib (>=3.10.0,<4.0.0)",
    "imagemagic (>=0.2.1,<0.3.0)",
]
```

### Operating system

_No response_

### Matplotlib Version

>3.10.0

### Matplotlib Backend

_No response_

### Python version

_No response_

### Jupyter version

_No response_

### Installation

pip


Discussion:

Patch:
diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py
index 2be61284073a..a87f00201124 100644
--- a/lib/matplotlib/animation.py
+++ b/lib/matplotlib/animation.py
@@ -496,7 +496,7 @@ def grab_frame(self, **savefig_kwargs):
             "RGBA", self.frame_size, buf.getbuffer(), "raw", "RGBA", 0, 1)
         if im.getextrema()[3][0] < 255:
             # This frame has transparency, so we'll just add it as is.
-            self._frame.append(im)
+            self._frames.append(im)
         else:
             # Without transparency, we switch to RGB mode, which converts to P mode a
             # little better if needed (specifically, this helps with GIF output.)


Test patch:
diff --git a/lib/matplotlib/tests/test_animation.py b/lib/matplotlib/tests/test_animation.py
index a2b35f802c78..0b5aeaaec889 100644
--- a/lib/matplotlib/tests/test_animation.py
+++ b/lib/matplotlib/tests/test_animation.py
@@ -13,6 +13,7 @@
 import matplotlib as mpl
 from matplotlib import pyplot as plt
 from matplotlib import animation
+from matplotlib.animation import PillowWriter
 from matplotlib.testing.decorators import check_figures_equal
 
 
@@ -526,3 +527,20 @@ def test_movie_writer_invalid_path(anim):
     with pytest.raises(FileNotFoundError, match=match_str):
         anim.save("/foo/bar/aardvark/thiscannotreallyexist.mp4",
                   writer=animation.FFMpegFileWriter())
+
+
+def test_animation_with_transparency():
+    """Test animation exhaustion with transparency using PillowWriter directly"""
+    fig, ax = plt.subplots()
+    rect = plt.Rectangle((0, 0), 1, 1, color='red', alpha=0.5)
+    ax.add_patch(rect)
+    ax.set_xlim(0, 1)
+    ax.set_ylim(0, 1)
+
+    writer = PillowWriter(fps=30)
+    writer.setup(fig, 'unused.gif', dpi=100)
+    writer.grab_frame(transparent=True)
+    frame = writer._frames[-1]
+    # Check that the alpha channel is not 255, so frame has transparency
+    assert frame.getextrema()[3][0] < 255
+    plt.close(fig)