Skip to content

Commit f8b14c8

Browse files
committed
add cache_frame_data kwarg into FuncAnimation. fixes #8528.
1 parent e7719da commit f8b14c8

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

lib/matplotlib/animation.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -1598,10 +1598,14 @@ def init_func() -> iterable_of_artists
15981598
blitting any animated artists will be drawn according to their zorder.
15991599
However, they will be drawn on top of any previous artists, regardless
16001600
of their zorder. Defaults to *False*.
1601+
1602+
cache_frame_data : bool, optional
1603+
Controls whether frame data is cached. Defaults to *True*.
1604+
Disabling cache might be helpful when frames contain large objects.
16011605
"""
16021606

16031607
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
1604-
save_count=None, **kwargs):
1608+
save_count=None, *args, cache_frame_data=True, **kwargs):
16051609
if fargs:
16061610
self._args = fargs
16071611
else:
@@ -1649,6 +1653,8 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
16491653
# for a single frame from init, which is not what we want.
16501654
self._save_seq = []
16511655

1656+
self._cache_frame_data = cache_frame_data
1657+
16521658
def new_frame_seq(self):
16531659
# Use the generating function to generate a new frame sequence
16541660
return self._iter_gen()
@@ -1703,8 +1709,9 @@ def _init_draw(self):
17031709
self._save_seq = []
17041710

17051711
def _draw_frame(self, framedata):
1706-
# Save the data for potential saving of movies.
1707-
self._save_seq.append(framedata)
1712+
if self._cache_frame_data:
1713+
# Save the data for potential saving of movies.
1714+
self._save_seq.append(framedata)
17081715

17091716
# Make sure to respect save_count (keep only the last save_count
17101717
# around)

lib/matplotlib/tests/test_animation.py

+49
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pathlib import Path
33
import subprocess
44
import sys
5+
import weakref
56

67
import numpy as np
78
from pathlib import Path
@@ -258,3 +259,51 @@ def test_failing_ffmpeg(tmpdir, monkeypatch):
258259
make_animation().save("test.mpeg")
259260
finally:
260261
animation.writers.reset_available_writers()
262+
263+
264+
@pytest.mark.parametrize("cache_frame_data, weakref_assertion_fn", [
265+
pytest.param(
266+
False, lambda ref: ref is None, id='cache_frame_data_is_disabled'),
267+
pytest.param(
268+
True, lambda ref: ref is not None, id='cache_frame_data_is_enabled'),
269+
])
270+
def test_funcanimation_holding_frames(cache_frame_data, weakref_assertion_fn):
271+
fig, ax = plt.subplots()
272+
line, = ax.plot([], [])
273+
274+
class Frame(dict):
275+
# this subclassing enables to use weakref.ref()
276+
pass
277+
278+
def init():
279+
line.set_data([], [])
280+
return line,
281+
282+
def animate(frame):
283+
line.set_data(frame['x'], frame['y'])
284+
return line,
285+
286+
frames_generated = []
287+
288+
def frames_generator():
289+
for _ in range(5):
290+
x = np.linspace(0, 10, 100)
291+
y = np.random.rand(100)
292+
293+
frame = Frame(x=x, y=y)
294+
295+
# collect weak references to frames
296+
# to validate their references later
297+
frames_generated.append(weakref.ref(frame))
298+
299+
yield frame
300+
301+
anim = animation.FuncAnimation(fig, animate, init_func=init,
302+
frames=frames_generator, save_count=5,
303+
cache_frame_data=cache_frame_data)
304+
305+
writer = NullMovieWriter()
306+
anim.save('unused.null', writer=writer)
307+
308+
for f in frames_generated:
309+
assert weakref_assertion_fn(f())

0 commit comments

Comments
 (0)