Skip to content

Commit 38434b9

Browse files
committed
Deprecate attributes and expire deprecation in animation
1 parent f0cd8de commit 38434b9

File tree

4 files changed

+37
-38
lines changed

4 files changed

+37
-38
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Passing None as ``save_count``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... to `.FuncAnimation` no longer limits the number of frames to 100. Make
5+
sure that it either can be inferred from *frames* or provide an integer
6+
*save_count*.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``Animation`` attributes
2+
~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The attributes ``repeat`` of `.TimedAnimation` and ``save_count`` of
5+
`.FuncAnimation` are considered private and deprecated.

lib/matplotlib/animation.py

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ def func(current_frame: int, total_frames: int) -> Any
10691069
frame_number = 0
10701070
# TODO: Currently only FuncAnimation has a save_count
10711071
# attribute. Can we generalize this to all Animations?
1072-
save_count_list = [getattr(a, 'save_count', None)
1072+
save_count_list = [getattr(a, '_save_count', None)
10731073
for a in all_anim]
10741074
if None in save_count_list:
10751075
total_frames = None
@@ -1222,7 +1222,7 @@ def to_html5_video(self, embed_limit=None):
12221222
This saves the animation as an h264 video, encoded in base64
12231223
directly into the HTML5 video tag. This respects :rc:`animation.writer`
12241224
and :rc:`animation.bitrate`. This also makes use of the
1225-
``interval`` to control the speed, and uses the ``repeat``
1225+
*interval* to control the speed, and uses the *repeat*
12261226
parameter to decide whether to loop.
12271227
12281228
Parameters
@@ -1285,7 +1285,7 @@ def to_html5_video(self, embed_limit=None):
12851285
options = ['controls', 'autoplay']
12861286

12871287
# If we're set to repeat, make it loop
1288-
if hasattr(self, 'repeat') and self.repeat:
1288+
if getattr(self, '_repeat', False):
12891289
options.append('loop')
12901290

12911291
return VIDEO_TAG.format(video=self._base64_video,
@@ -1306,17 +1306,18 @@ def to_jshtml(self, fps=None, embed_frames=True, default_mode=None):
13061306
embed_frames : bool, optional
13071307
default_mode : str, optional
13081308
What to do when the animation ends. Must be one of ``{'loop',
1309-
'once', 'reflect'}``. Defaults to ``'loop'`` if ``self.repeat``
1310-
is True, otherwise ``'once'``.
1309+
'once', 'reflect'}``. Defaults to ``'loop'`` if the *repeat*
1310+
parameter is True, otherwise ``'once'``.
13111311
"""
13121312
if fps is None and hasattr(self, '_interval'):
13131313
# Convert interval in ms to frames per second
13141314
fps = 1000 / self._interval
13151315

13161316
# If we're not given a default mode, choose one base on the value of
1317-
# the repeat attribute
1317+
# the _repeat attribute
13181318
if default_mode is None:
1319-
default_mode = 'loop' if self.repeat else 'once'
1319+
default_mode = 'loop' if getattr(self, '_repeat',
1320+
False) else 'once'
13201321

13211322
if not hasattr(self, "_html_representation"):
13221323
# Can't open a NamedTemporaryFile twice on Windows, so use a
@@ -1380,13 +1381,12 @@ class TimedAnimation(Animation):
13801381
blit : bool, default: False
13811382
Whether blitting is used to optimize drawing.
13821383
"""
1383-
13841384
def __init__(self, fig, interval=200, repeat_delay=0, repeat=True,
13851385
event_source=None, *args, **kwargs):
13861386
self._interval = interval
13871387
# Undocumented support for repeat_delay = None as backcompat.
13881388
self._repeat_delay = repeat_delay if repeat_delay is not None else 0
1389-
self.repeat = repeat
1389+
self._repeat = repeat
13901390
# If we're not given an event source, create a new timer. This permits
13911391
# sharing timers between animation objects for syncing animations.
13921392
if event_source is None:
@@ -1403,7 +1403,7 @@ def _step(self, *args):
14031403
# back.
14041404
still_going = super()._step(*args)
14051405
if not still_going:
1406-
if self.repeat:
1406+
if self._repeat:
14071407
# Restart the draw loop
14081408
self._init_draw()
14091409
self.frame_seq = self.new_frame_seq()
@@ -1423,6 +1423,8 @@ def _step(self, *args):
14231423
self.event_source.interval = self._interval
14241424
return True
14251425

1426+
repeat = _api.deprecated("3.7")(property(lambda self: self._repeat))
1427+
14261428

14271429
class ArtistAnimation(TimedAnimation):
14281430
"""
@@ -1564,7 +1566,7 @@ def init_func() -> iterable_of_artists
15641566
fargs : tuple or None, optional
15651567
Additional arguments to pass to each call to *func*.
15661568
1567-
save_count : int, default: 100
1569+
save_count : int, optional
15681570
Fallback for the number of values from *frames* to cache. This is
15691571
only used if the number of frames cannot be inferred from *frames*,
15701572
i.e. when it's an iterator without length or a generator.
@@ -1589,7 +1591,6 @@ def init_func() -> iterable_of_artists
15891591
Whether frame data is cached. Disabling cache might be helpful when
15901592
frames contain large objects.
15911593
"""
1592-
15931594
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
15941595
save_count=None, *, cache_frame_data=True, **kwargs):
15951596
if fargs:
@@ -1602,7 +1603,7 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
16021603
# Amount of framedata to keep around for saving movies. This is only
16031604
# used if we don't know how many frames there will be: in the case
16041605
# of no generator or in the case of a callable.
1605-
self.save_count = save_count
1606+
self._save_count = save_count
16061607
# Set up a function that creates a new iterable when needed. If nothing
16071608
# is passed in for frames, just use itertools.count, which will just
16081609
# keep counting from 0. A callable passed in for frames is assumed to
@@ -1622,19 +1623,10 @@ def iter_frames(frames=frames):
16221623
else:
16231624
self._iter_gen = lambda: iter(frames)
16241625
if hasattr(frames, '__len__'):
1625-
self.save_count = len(frames)
1626+
self._save_count = len(frames)
16261627
else:
16271628
self._iter_gen = lambda: iter(range(frames))
1628-
self.save_count = frames
1629-
1630-
if self.save_count is None:
1631-
# If we're passed in and using the default, set save_count to 100.
1632-
self.save_count = 100
1633-
else:
1634-
# itertools.islice returns an error when passed a numpy int instead
1635-
# of a native python int (https://bugs.python.org/issue30537).
1636-
# As a workaround, convert save_count to a native python int.
1637-
self.save_count = int(self.save_count)
1629+
self._save_count = frames
16381630

16391631
self._cache_frame_data = cache_frame_data
16401632

@@ -1661,26 +1653,18 @@ def new_saved_frame_seq(self):
16611653
self._old_saved_seq = list(self._save_seq)
16621654
return iter(self._old_saved_seq)
16631655
else:
1664-
if self.save_count is not None:
1665-
return itertools.islice(self.new_frame_seq(), self.save_count)
1666-
1667-
else:
1656+
if self._save_count is None:
16681657
frame_seq = self.new_frame_seq()
16691658

16701659
def gen():
16711660
try:
1672-
for _ in range(100):
1661+
while True:
16731662
yield next(frame_seq)
16741663
except StopIteration:
16751664
pass
1676-
else:
1677-
_api.warn_deprecated(
1678-
"2.2", message="FuncAnimation.save has truncated "
1679-
"your animation to 100 frames. In the future, no "
1680-
"such truncation will occur; please pass "
1681-
"'save_count' accordingly.")
1682-
16831665
return gen()
1666+
else:
1667+
return itertools.islice(self.new_frame_seq(), self._save_count)
16841668

16851669
def _init_draw(self):
16861670
super()._init_draw()
@@ -1721,7 +1705,8 @@ def _draw_frame(self, framedata):
17211705

17221706
# Make sure to respect save_count (keep only the last save_count
17231707
# around)
1724-
self._save_seq = self._save_seq[-self.save_count:]
1708+
if self._save_count is not None:
1709+
self._save_seq = self._save_seq[-self._save_count:]
17251710

17261711
# Call the func with framedata and args. If blitting is desired,
17271712
# func needs to return a sequence of any artists that were modified.
@@ -1747,3 +1732,6 @@ def _draw_frame(self, framedata):
17471732

17481733
for a in self._drawn_artists:
17491734
a.set_animated(self._blit)
1735+
1736+
save_count = _api.deprecated("3.7")(
1737+
property(lambda self: self._save_count))

lib/matplotlib/tests/test_animation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def test_null_movie_writer(anim):
8383
assert writer.dpi == dpi
8484
assert writer.args == ()
8585
assert writer.savefig_kwargs == savefig_kwargs
86-
assert writer._count == anim.save_count
86+
assert writer._count == anim._save_count
8787

8888

8989
@pytest.mark.parametrize('anim', [dict(klass=dict)], indirect=['anim'])

0 commit comments

Comments
 (0)