@@ -247,7 +247,7 @@ class MovieWriter(AbstractMovieWriter):
247247 The format used in writing frame data, defaults to 'rgba'.
248248 fig : `~matplotlib.figure.Figure`
249249 The figure to capture data from.
250- This must be provided by the sub-classes .
250+ This must be provided by the subclasses .
251251 """
252252
253253 # Builtin writer subclasses additionally define the _exec_key and _args_key
@@ -1083,7 +1083,7 @@ def _pre_composite_to_white(color):
10831083 frame_number = 0
10841084 # TODO: Currently only FuncAnimation has a save_count
10851085 # attribute. Can we generalize this to all Animations?
1086- save_count_list = [getattr (a , 'save_count ' , None )
1086+ save_count_list = [getattr (a , '_save_count ' , None )
10871087 for a in all_anim ]
10881088 if None in save_count_list :
10891089 total_frames = None
@@ -1236,7 +1236,7 @@ def to_html5_video(self, embed_limit=None):
12361236 This saves the animation as an h264 video, encoded in base64
12371237 directly into the HTML5 video tag. This respects :rc:`animation.writer`
12381238 and :rc:`animation.bitrate`. This also makes use of the
1239- `` interval`` to control the speed, and uses the `` repeat``
1239+ * interval* to control the speed, and uses the * repeat*
12401240 parameter to decide whether to loop.
12411241
12421242 Parameters
@@ -1299,7 +1299,7 @@ def to_html5_video(self, embed_limit=None):
12991299 options = ['controls' , 'autoplay' ]
13001300
13011301 # If we're set to repeat, make it loop
1302- if hasattr (self , 'repeat' ) and self . repeat :
1302+ if getattr (self , '_repeat' , False ) :
13031303 options .append ('loop' )
13041304
13051305 return VIDEO_TAG .format (video = self ._base64_video ,
@@ -1320,17 +1320,18 @@ def to_jshtml(self, fps=None, embed_frames=True, default_mode=None):
13201320 embed_frames : bool, optional
13211321 default_mode : str, optional
13221322 What to do when the animation ends. Must be one of ``{'loop',
1323- 'once', 'reflect'}``. Defaults to ``'loop'`` if ``self. repeat``
1324- is True, otherwise ``'once'``.
1323+ 'once', 'reflect'}``. Defaults to ``'loop'`` if the * repeat*
1324+ parameter is True, otherwise ``'once'``.
13251325 """
13261326 if fps is None and hasattr (self , '_interval' ):
13271327 # Convert interval in ms to frames per second
13281328 fps = 1000 / self ._interval
13291329
13301330 # If we're not given a default mode, choose one base on the value of
1331- # the repeat attribute
1331+ # the _repeat attribute
13321332 if default_mode is None :
1333- default_mode = 'loop' if self .repeat else 'once'
1333+ default_mode = 'loop' if getattr (self , '_repeat' ,
1334+ False ) else 'once'
13341335
13351336 if not hasattr (self , "_html_representation" ):
13361337 # Can't open a NamedTemporaryFile twice on Windows, so use a
@@ -1394,13 +1395,12 @@ class TimedAnimation(Animation):
13941395 blit : bool, default: False
13951396 Whether blitting is used to optimize drawing.
13961397 """
1397-
13981398 def __init__ (self , fig , interval = 200 , repeat_delay = 0 , repeat = True ,
13991399 event_source = None , * args , ** kwargs ):
14001400 self ._interval = interval
14011401 # Undocumented support for repeat_delay = None as backcompat.
14021402 self ._repeat_delay = repeat_delay if repeat_delay is not None else 0
1403- self .repeat = repeat
1403+ self ._repeat = repeat
14041404 # If we're not given an event source, create a new timer. This permits
14051405 # sharing timers between animation objects for syncing animations.
14061406 if event_source is None :
@@ -1417,7 +1417,7 @@ def _step(self, *args):
14171417 # back.
14181418 still_going = super ()._step (* args )
14191419 if not still_going :
1420- if self .repeat :
1420+ if self ._repeat :
14211421 # Restart the draw loop
14221422 self ._init_draw ()
14231423 self .frame_seq = self .new_frame_seq ()
@@ -1437,6 +1437,8 @@ def _step(self, *args):
14371437 self .event_source .interval = self ._interval
14381438 return True
14391439
1440+ repeat = _api .deprecate_privatize_attribute ("3.7" )
1441+
14401442
14411443class ArtistAnimation (TimedAnimation ):
14421444 """
@@ -1593,7 +1595,7 @@ def init_func() -> iterable_of_artists
15931595 Additional arguments to pass to each call to *func*. Note: the use of
15941596 `functools.partial` is preferred over *fargs*. See *func* for details.
15951597
1596- save_count : int, default: 100
1598+ save_count : int, optional
15971599 Fallback for the number of values from *frames* to cache. This is
15981600 only used if the number of frames cannot be inferred from *frames*,
15991601 i.e. when it's an iterator without length or a generator.
@@ -1618,7 +1620,6 @@ def init_func() -> iterable_of_artists
16181620 Whether frame data is cached. Disabling cache might be helpful when
16191621 frames contain large objects.
16201622 """
1621-
16221623 def __init__ (self , fig , func , frames = None , init_func = None , fargs = None ,
16231624 save_count = None , * , cache_frame_data = True , ** kwargs ):
16241625 if fargs :
@@ -1631,7 +1632,7 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
16311632 # Amount of framedata to keep around for saving movies. This is only
16321633 # used if we don't know how many frames there will be: in the case
16331634 # of no generator or in the case of a callable.
1634- self .save_count = save_count
1635+ self ._save_count = save_count
16351636 # Set up a function that creates a new iterable when needed. If nothing
16361637 # is passed in for frames, just use itertools.count, which will just
16371638 # keep counting from 0. A callable passed in for frames is assumed to
@@ -1651,19 +1652,31 @@ def iter_frames(frames=frames):
16511652 else :
16521653 self ._iter_gen = lambda : iter (frames )
16531654 if hasattr (frames , '__len__' ):
1654- self .save_count = len (frames )
1655+ self ._save_count = len (frames )
1656+ if save_count is not None :
1657+ _api .warn_external (
1658+ f"You passed in an explicit { save_count = } "
1659+ "which is being ignored in favor of "
1660+ f"{ len (frames )= } ."
1661+ )
16551662 else :
16561663 self ._iter_gen = lambda : iter (range (frames ))
1657- self .save_count = frames
1658-
1659- if self .save_count is None :
1660- # If we're passed in and using the default, set save_count to 100.
1661- self .save_count = 100
1662- else :
1663- # itertools.islice returns an error when passed a numpy int instead
1664- # of a native python int (https://bugs.python.org/issue30537).
1665- # As a workaround, convert save_count to a native python int.
1666- self .save_count = int (self .save_count )
1664+ self ._save_count = frames
1665+ if save_count is not None :
1666+ _api .warn_external (
1667+ f"You passed in an explicit { save_count = } which is being "
1668+ f"ignored in favor of { frames = } ."
1669+ )
1670+ if self ._save_count is None and cache_frame_data :
1671+ _api .warn_external (
1672+ f"{ frames = !r} which we can infer the length of, "
1673+ "did not pass an explicit *save_count* "
1674+ f"and passed { cache_frame_data = } . To avoid a possibly "
1675+ "unbounded cache, frame data caching has been disabled. "
1676+ "To suppress this warning either pass "
1677+ "`cache_frame_data=False` or `save_count=MAX_FRAMES`."
1678+ )
1679+ cache_frame_data = False
16671680
16681681 self ._cache_frame_data = cache_frame_data
16691682
@@ -1690,26 +1703,18 @@ def new_saved_frame_seq(self):
16901703 self ._old_saved_seq = list (self ._save_seq )
16911704 return iter (self ._old_saved_seq )
16921705 else :
1693- if self .save_count is not None :
1694- return itertools .islice (self .new_frame_seq (), self .save_count )
1695-
1696- else :
1706+ if self ._save_count is None :
16971707 frame_seq = self .new_frame_seq ()
16981708
16991709 def gen ():
17001710 try :
1701- for _ in range ( 100 ) :
1711+ while True :
17021712 yield next (frame_seq )
17031713 except StopIteration :
17041714 pass
1705- else :
1706- _api .warn_deprecated (
1707- "2.2" , message = "FuncAnimation.save has truncated "
1708- "your animation to 100 frames. In the future, no "
1709- "such truncation will occur; please pass "
1710- "'save_count' accordingly." )
1711-
17121715 return gen ()
1716+ else :
1717+ return itertools .islice (self .new_frame_seq (), self ._save_count )
17131718
17141719 def _init_draw (self ):
17151720 super ()._init_draw ()
@@ -1747,10 +1752,7 @@ def _draw_frame(self, framedata):
17471752 if self ._cache_frame_data :
17481753 # Save the data for potential saving of movies.
17491754 self ._save_seq .append (framedata )
1750-
1751- # Make sure to respect save_count (keep only the last save_count
1752- # around)
1753- self ._save_seq = self ._save_seq [- self .save_count :]
1755+ self ._save_seq = self ._save_seq [- self ._save_count :]
17541756
17551757 # Call the func with framedata and args. If blitting is desired,
17561758 # func needs to return a sequence of any artists that were modified.
@@ -1776,3 +1778,5 @@ def _draw_frame(self, framedata):
17761778
17771779 for a in self ._drawn_artists :
17781780 a .set_animated (self ._blit )
1781+
1782+ save_count = _api .deprecate_privatize_attribute ("3.7" )
0 commit comments