Skip to content

Traceback from animation.MovieWriter.saving method is confusing because it provides no useful information #8760

Closed
@krrk

Description

@krrk

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:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions