Skip to content

Commit 7c7201c

Browse files
committed
Deprecate attributes and expire deprecation in animation
1 parent 924e210 commit 7c7201c

File tree

4 files changed

+28
-43
lines changed

4 files changed

+28
-43
lines changed
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.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Passing None as ``save_count``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... to `.FuncAnimation` is no longer possible.

lib/matplotlib/animation.py

Lines changed: 18 additions & 42 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,8 +1306,8 @@ 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
@@ -1316,7 +1316,8 @@ def to_jshtml(self, fps=None, embed_frames=True, default_mode=None):
13161316
# If we're not given a default mode, choose one base on the value of
13171317
# 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
@@ -1381,12 +1382,14 @@ class TimedAnimation(Animation):
13811382
Whether blitting is used to optimize drawing.
13821383
"""
13831384

1385+
repeat = _api.deprecate_privatize_attribute("3.7")
1386+
13841387
def __init__(self, fig, interval=200, repeat_delay=0, repeat=True,
13851388
event_source=None, *args, **kwargs):
13861389
self._interval = interval
13871390
# Undocumented support for repeat_delay = None as backcompat.
13881391
self._repeat_delay = repeat_delay if repeat_delay is not None else 0
1389-
self.repeat = repeat
1392+
self._repeat = repeat
13901393
# If we're not given an event source, create a new timer. This permits
13911394
# sharing timers between animation objects for syncing animations.
13921395
if event_source is None:
@@ -1403,7 +1406,7 @@ def _step(self, *args):
14031406
# back.
14041407
still_going = super()._step(*args)
14051408
if not still_going:
1406-
if self.repeat:
1409+
if self._repeat:
14071410
# Restart the draw loop
14081411
self._init_draw()
14091412
self.frame_seq = self.new_frame_seq()
@@ -1589,9 +1592,10 @@ def init_func() -> iterable_of_artists
15891592
Whether frame data is cached. Disabling cache might be helpful when
15901593
frames contain large objects.
15911594
"""
1595+
save_count = _api.deprecate_privatize_attribute("3.7")
15921596

15931597
def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
1594-
save_count=None, *, cache_frame_data=True, **kwargs):
1598+
save_count=100, *, cache_frame_data=True, **kwargs):
15951599
if fargs:
15961600
self._args = fargs
15971601
else:
@@ -1602,7 +1606,7 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
16021606
# Amount of framedata to keep around for saving movies. This is only
16031607
# used if we don't know how many frames there will be: in the case
16041608
# of no generator or in the case of a callable.
1605-
self.save_count = save_count
1609+
self._save_count = save_count
16061610
# Set up a function that creates a new iterable when needed. If nothing
16071611
# is passed in for frames, just use itertools.count, which will just
16081612
# keep counting from 0. A callable passed in for frames is assumed to
@@ -1622,19 +1626,10 @@ def iter_frames(frames=frames):
16221626
else:
16231627
self._iter_gen = lambda: iter(frames)
16241628
if hasattr(frames, '__len__'):
1625-
self.save_count = len(frames)
1629+
self._save_count = len(frames)
16261630
else:
16271631
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)
1632+
self._save_count = frames
16381633

16391634
self._cache_frame_data = cache_frame_data
16401635

@@ -1661,26 +1656,7 @@ def new_saved_frame_seq(self):
16611656
self._old_saved_seq = list(self._save_seq)
16621657
return iter(self._old_saved_seq)
16631658
else:
1664-
if self.save_count is not None:
1665-
return itertools.islice(self.new_frame_seq(), self.save_count)
1666-
1667-
else:
1668-
frame_seq = self.new_frame_seq()
1669-
1670-
def gen():
1671-
try:
1672-
for _ in range(100):
1673-
yield next(frame_seq)
1674-
except StopIteration:
1675-
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-
1683-
return gen()
1659+
return itertools.islice(self.new_frame_seq(), self._save_count)
16841660

16851661
def _init_draw(self):
16861662
super()._init_draw()
@@ -1721,7 +1697,7 @@ def _draw_frame(self, framedata):
17211697

17221698
# Make sure to respect save_count (keep only the last save_count
17231699
# around)
1724-
self._save_seq = self._save_seq[-self.save_count:]
1700+
self._save_seq = self._save_seq[-self._save_count:]
17251701

17261702
# Call the func with framedata and args. If blitting is desired,
17271703
# func needs to return a sequence of any artists that were modified.

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)