beetbox / audioread

cross-library (GStreamer + Core Audio + MAD + FFmpeg) audio decoding for Python
MIT License
481 stars 108 forks source link

audioread-2.1.7 avconv not found #93

Closed MarvinT closed 5 years ago

MarvinT commented 5 years ago

travis-ci switched my repo over to installing audioread-2.1.7 today and as a result I'm getting a new error from it. Example failing build: https://travis-ci.com/MarvinT/morphs/jobs/201073844 Example passing build from yesterday: https://travis-ci.com/MarvinT/morphs/jobs/200651954 Stack trace is below:

morphs/load/wavfile.py:20: in load_wav
    data, sr = librosa.core.load(file.as_posix(), sr=None)
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/librosa/core/audio.py:119: in load
    with audioread.audio_open(os.path.realpath(path)) as input_file:
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/audioread/__init__.py:107: in audio_open
    backends = available_backends()
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/audioread/__init__.py:86: in available_backends
    if ffdec.available():
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/audioread/ffdec.py:108: in available
    creationflags=PROC_FLAGS,
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/audioread/ffdec.py:94: in popen_multiple
    return subprocess.Popen(cmd, *args, **kwargs)
/opt/python/3.6.3/lib/python3.6/subprocess.py:709: in __init__
    restore_signals, start_new_session)
self = <subprocess.Popen object at 0x7f33a91562e8>
args = ['avconv', '-version'], executable = b'avconv', preexec_fn = None
close_fds = True, pass_fds = (), cwd = None, env = None, startupinfo = None
creationflags = 0, shell = False, p2cread = -1, p2cwrite = -1, c2pread = 24
c2pwrite = 25, errread = 26, errwrite = 27, restore_signals = True
start_new_session = False
    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals, start_new_session):
        """Execute program (POSIX version)"""

        if isinstance(args, (str, bytes)):
            args = [args]
        else:
            args = list(args)

        if shell:
            args = ["/bin/sh", "-c"] + args
            if executable:
                args[0] = executable

        if executable is None:
            executable = args[0]
        orig_executable = executable

        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()

                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _posixsubprocess.fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session, preexec_fn)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)

            # self._devnull is not always defined.
            devnull_fd = getattr(self, '_devnull', None)
            if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd:
                os.close(p2cread)
            if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd:
                os.close(c2pwrite)
            if errwrite != -1 and errread != -1 and errwrite != devnull_fd:
                os.close(errwrite)
            if devnull_fd is not None:
                os.close(devnull_fd)
            # Prevent a double close of these fds from __init__ on error.
            self._closed_child_pipe_fds = True

            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)

        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass

            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                child_exec_never_called = (err_msg == "noexec")
                if child_exec_never_called:
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                    if errno_num == errno.ENOENT:
                        err_msg += ': ' + repr(err_filename)
>               raise child_exception_type(errno_num, err_msg, err_filename)
E               FileNotFoundError: [Errno 2] No such file or directory: 'avconv': 'avconv'
/opt/python/3.6.3/lib/python3.6/subprocess.py:1344: FileNotFoundError
sampsyo commented 5 years ago

Thanks for reporting this! Looks like a regression from #83. The available function in ffdec was not suitably guarded with an exception handler. Looking into it now…

sampsyo commented 5 years ago

I've pushed what I believe to be a fix. Can you give that a try and confirm that it fixes the problem?

MarvinT commented 5 years ago

Sorry, I wasn't installing from source... or even specifying to install audioread. I'm just using it through librosa and wanted to report the error. I hopefully solved it by adding but we'll see if I can get the tests to pass.

before_install:
  - sudo apt-get install libav-tools

to my .travis-ci.yml

sampsyo commented 5 years ago

OK! Let us know if you find a way to reproduce this in an environment where you can install from source and can confirm its fixed.