Description
Enhancement
Summary
The MovieWriter.saving method produces cryptic tracebacks when you pass invalid options to the underlying utility (FFMPEG in my situation). The tracebacks contain no useful information as the error is truly in the subprocess. It would be ideal to catch the error and raise the error message from the utility running in the subprocess.
Code for reproduction
An example of an invalid option for ffmpeg is as follows. First create an Animation
called ani
and then:
ani.save('animation.mp4', writer='ffmpeg', codec='rawvideo')
Actual outcome
ValueError Traceback (most recent call last)
<ipython-input-99-2b083d816b1a> in <module>()
----> 1 ani.save('animation.mp4', codec='rawvideo')
/usr/local/lib/python3.5/dist-packages/matplotlib/animation.py in save(self, filename, writer, fps, dpi, codec, bitrate, extra_args, metadata, extra_anim, savefig_kwargs)
1061 # TODO: See if turning off blit is really necessary
1062 anim._draw_next_frame(d, blit=False)
-> 1063 writer.grab_frame(**savefig_kwargs)
1064
1065 # Reconnect signal for first draw if necessary
/usr/lib/python3.5/contextlib.py in __exit__(self, type, value, traceback)
75 value = type()
76 try:
---> 77 self.gen.throw(type, value, traceback)
78 raise RuntimeError("generator didn't stop after throw()")
79 except StopIteration as exc:
/usr/local/lib/python3.5/dist-packages/matplotlib/animation.py in saving(self, *args, **kw)
287 yield self
288 finally:
--> 289 self.finish()
290
291 def _run(self):
/usr/local/lib/python3.5/dist-packages/matplotlib/animation.py in finish(self)
307 def finish(self):
308 'Finish any processing for writing the movie.'
--> 309 self.cleanup()
310
311 def grab_frame(self, **savefig_kwargs):
/usr/local/lib/python3.5/dist-packages/matplotlib/animation.py in cleanup(self)
346 def cleanup(self):
347 'Clean-up and collect the process used to write the movie file.'
--> 348 out, err = self._proc.communicate()
349 self._frame_sink().close()
350 verbose.report('MovieWriter -- '
/usr/lib/python3.5/subprocess.py in communicate(self, input, timeout)
1070
1071 try:
-> 1072 stdout, stderr = self._communicate(input, endtime, timeout)
1073 finally:
1074 self._communication_started = True
/usr/lib/python3.5/subprocess.py in _communicate(self, input, endtime, orig_timeout)
1704 selector.register(self.stdin, selectors.EVENT_WRITE)
1705 if self.stdout:
-> 1706 selector.register(self.stdout, selectors.EVENT_READ)
1707 if self.stderr:
1708 selector.register(self.stderr, selectors.EVENT_READ)
/usr/lib/python3.5/selectors.py in register(self, fileobj, events, data)
349
350 def register(self, fileobj, events, data=None):
--> 351 key = super().register(fileobj, events, data)
352 poll_events = 0
353 if events & EVENT_READ:
/usr/lib/python3.5/selectors.py in register(self, fileobj, events, data)
235 raise ValueError("Invalid events: {!r}".format(events))
236
--> 237 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
238
239 if key.fd in self._fd_to_key:
/usr/lib/python3.5/selectors.py in _fileobj_lookup(self, fileobj)
222 """
223 try:
--> 224 return _fileobj_to_fd(fileobj)
225 except ValueError:
226 # Do an exhaustive search.
/usr/lib/python3.5/selectors.py in _fileobj_to_fd(fileobj)
37 except (AttributeError, TypeError, ValueError):
38 raise ValueError("Invalid file object: "
---> 39 "{!r}".format(fileobj)) from None
40 if fd < 0:
41 raise ValueError("Invalid file descriptor: {}".format(fd))
ValueError: Invalid file object: <_io.BufferedReader name=74>
Expected outcome
This saving operation fails because the rawvideo
codec is not supported in an mp4
container. matplotlib ought to output the following error message from ffmpeg in this scenario:
[mp4 @ 0x20f9fc0] Could not find tag for codec rawvideo in stream #0, codec not currently supported in container
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
By default ffmpeg is quite verbose and it outputs all information to stderr
. The output can be adjusted to just show these error messages by specifying -loglevel 16
.
Matplotlib version
- Operating System: Ubuntu 16.04.2
- Matplotlib Version: 2.0.2
- Python Version: 3.5
- Jupyter Version (if applicable): 4.2.3
- Other Libraries: