Skip to content

Commit 02e5358

Browse files
committed
Don't use stdlib private API in animation.py.
The _std{out,err}_buff attributes on Popen objects don't exist anymore on Unices (since CPython 3.4). As these buffers are not accessible anymore in finish(), move the status check and possible exception throwing to cleanup(), where they are still available. Don't silently drop exceptions; this shows that ProcessStandin previously used by HTMLWriter was also buggy (communicate() needed to return bytes, not str); instead of using it, just do all the work in finish(), similarly to PillowWriter).
1 parent 7bcf618 commit 02e5358

File tree

1 file changed

+18
-32
lines changed

1 file changed

+18
-32
lines changed

lib/matplotlib/animation.py

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import abc
2020
import base64
2121
import contextlib
22-
from io import BytesIO
22+
from io import BytesIO, TextIOWrapper
2323
import itertools
2424
import logging
2525
import os
@@ -346,12 +346,11 @@ def _run(self):
346346
# movie file. *args* returns the sequence of command line arguments
347347
# from a few configuration options.
348348
command = self._args()
349-
output = subprocess.PIPE
350349
_log.info('MovieWriter.run: running command: %s', command)
351-
self._proc = subprocess.Popen(command, shell=False,
352-
stdout=output, stderr=output,
353-
stdin=subprocess.PIPE,
354-
creationflags=subprocess_creation_flags)
350+
PIPE = subprocess.PIPE
351+
self._proc = subprocess.Popen(
352+
command, stdin=PIPE, stdout=PIPE, stderr=PIPE,
353+
creationflags=subprocess_creation_flags)
355354

356355
def finish(self):
357356
'''Finish any processing for writing the movie.'''
@@ -394,8 +393,18 @@ def cleanup(self):
394393
'''Clean-up and collect the process used to write the movie file.'''
395394
out, err = self._proc.communicate()
396395
self._frame_sink().close()
397-
_log.debug('MovieWriter -- Command stdout:\n%s', out)
398-
_log.debug('MovieWriter -- Command stderr:\n%s', err)
396+
# Use the encoding/errors that universal_newlines would use.
397+
out = TextIOWrapper(BytesIO(out)).read()
398+
err = TextIOWrapper(BytesIO(err)).read()
399+
_log.debug("MovieWriter stdout:\n%s", out)
400+
_log.debug("MovieWriter stderr:\n%s", err)
401+
if self._proc.returncode:
402+
raise RuntimeError('Error creating movie, return code {}\n'
403+
'stdout:\n'
404+
'{}\n'
405+
'stderr:\n'
406+
'{}\n'
407+
.format(self._proc.returncode, out, err))
399408

400409
@classmethod
401410
def bin_path(cls):
@@ -520,19 +529,6 @@ def finish(self):
520529
self._run()
521530
MovieWriter.finish(self) # Will call clean-up
522531

523-
# Check error code for creating file here, since we just run
524-
# the process here, rather than having an open pipe.
525-
if self._proc.returncode:
526-
try:
527-
stdout = [s.decode() for s in self._proc._stdout_buff]
528-
stderr = [s.decode() for s in self._proc._stderr_buff]
529-
_log.info("MovieWriter.finish: stdout: %s", stdout)
530-
_log.info("MovieWriter.finish: stderr: %s", stderr)
531-
except Exception as e:
532-
pass
533-
raise RuntimeError('Error creating movie, return code: {}'
534-
.format(self._proc.returncode))
535-
536532
def cleanup(self):
537533
MovieWriter.cleanup(self)
538534

@@ -894,17 +890,7 @@ def grab_frame(self, **savefig_kwargs):
894890
else:
895891
return super().grab_frame(**savefig_kwargs)
896892

897-
def _run(self):
898-
# make a duck-typed subprocess stand in
899-
# this is called by the MovieWriter base class, but not used here.
900-
class ProcessStandin(object):
901-
returncode = 0
902-
903-
def communicate(self):
904-
return '', ''
905-
906-
self._proc = ProcessStandin()
907-
893+
def finish(self):
908894
# save the frames to an html file
909895
if self.embed_frames:
910896
fill_frames = _embedded_frames(self._saved_frames,

0 commit comments

Comments
 (0)