diff --git a/beets/util/__init__.py b/beets/util/__init__.py index 6074c557b..3448d104f 100644 --- a/beets/util/__init__.py +++ b/beets/util/__init__.py @@ -22,6 +22,7 @@ import shutil import fnmatch from collections import defaultdict import traceback +import subprocess MAX_FILENAME_LENGTH = 200 WINDOWS_MAGIC_PREFIX = u'\\\\?\\' @@ -573,3 +574,20 @@ def cpu_count(): return num else: return 1 + +def command_output(cmd): + """Wraps the `subprocess` module to invoke a command (given as a + list of arguments starting with the command name) and collect + stdout. The stderr stream is ignored. May raise + `subprocess.CalledProcessError` or an `OSError`. + + This replaces `subprocess.check_output`, which isn't available in + Python 2.6 and which can have problems if lots of output is sent to + stderr. + """ + with open(os.devnull, 'w') as devnull: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=devnull) + stdout, _ = proc.communicate() + if proc.returncode: + raise subprocess.CalledProcessError(proc.returncode, cmd) + return stdout diff --git a/beets/util/artresizer.py b/beets/util/artresizer.py index 6a5c6db0b..b97b28e2f 100644 --- a/beets/util/artresizer.py +++ b/beets/util/artresizer.py @@ -20,6 +20,7 @@ import subprocess import os from tempfile import NamedTemporaryFile import logging +from beets.util import command_output # Resizing methods PIL = 1 @@ -42,8 +43,7 @@ def call(args): command exits abnormally, a ArtResizerError is raised. """ try: - with open(os.devnull, 'w') as devnull: - return subprocess.check_output(args, stderr=devnull) + return command_output(args) except subprocess.CalledProcessError as e: raise ArtResizerError( "{0} exited with status {1}".format(args[0], e.returncode) diff --git a/beetsplug/convert.py b/beetsplug/convert.py index 4ea3b60e3..2afa658a7 100644 --- a/beetsplug/convert.py +++ b/beetsplug/convert.py @@ -21,7 +21,7 @@ import threading from subprocess import Popen, PIPE from beets.plugins import BeetsPlugin -from beets import ui, library, util +from beets import ui, util from beetsplug.embedart import _embed log = logging.getLogger('beets') diff --git a/beetsplug/replaygain.py b/beetsplug/replaygain.py index 1f03a4ed8..f7e041634 100755 --- a/beetsplug/replaygain.py +++ b/beetsplug/replaygain.py @@ -18,7 +18,7 @@ import os from beets import ui from beets.plugins import BeetsPlugin -from beets.util import syspath +from beets.util import syspath, command_output from beets.ui import commands log = logging.getLogger('beets') @@ -30,13 +30,11 @@ class ReplayGainError(Exception): """ def call(args): - """Execute the command indicated by `args` (a list of strings) and - return the command's output. The stderr stream is ignored. If the - command exits abnormally, a ReplayGainError is raised. + """Execute the command and return its output or raise a + ReplayGainError on failure. """ try: - with open(os.devnull, 'w') as devnull: - return subprocess.check_output(args, stderr=devnull) + return command_output(args) except subprocess.CalledProcessError as e: raise ReplayGainError( "{0} exited with status {1}".format(args[0], e.returncode)