diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index f3431c181..5522ae4b7 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -110,6 +110,27 @@ def print_(*strings): txt = txt.encode(_encoding(), 'replace') print(txt) +def input_(prompt=None): + """Like `raw_input`, but decodes the result to a Unicode string. + Raises a UserError if stdin is not available. The prompt is sent to + stdout rather than stderr. A printed between the prompt and the + input cursor. + """ + # raw_input incorrectly sends prompts to stderr, not stdout, so we + # use print() explicitly to display prompts. + # http://bugs.python.org/issue1927 + if prompt: + if isinstance(prompt, unicode): + prompt = prompt.encode(_encoding(), 'replace') + print(prompt, end=' ') + + try: + resp = raw_input() + except EOFError: + raise UserError('stdin stream ended while input required') + + return resp.decode(sys.stdin.encoding, 'ignore') + def input_options(options, require=False, prompt=None, fallback_prompt=None, numrange=None, default=None, color=False, max_width=72): """Prompts a user for input. The sequence of `options` defines the @@ -241,11 +262,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, fallback_prompt += '%i-%i, ' % numrange fallback_prompt += ', '.join(display_letters) + ':' - # raw_input incorrectly sends prompts to stderr, not stdout, so we - # use print() explicitly to display prompts. - # http://bugs.python.org/issue1927 - print(prompt, end=' ') - resp = raw_input() + resp = input_(prompt) while True: resp = resp.strip().lower() @@ -273,8 +290,7 @@ def input_options(options, require=False, prompt=None, fallback_prompt=None, return resp # Prompt for new input. - print(fallback_prompt, end=' ') - resp = raw_input() + resp = input_(fallback_prompt) def input_yn(prompt, require=False, color=False): """Prompts the user for a "yes" or "no" response. The default is diff --git a/beets/ui/commands.py b/beets/ui/commands.py index 3146275a8..4b1c108a3 100644 --- a/beets/ui/commands.py +++ b/beets/ui/commands.py @@ -25,7 +25,7 @@ import itertools import re from beets import ui -from beets.ui import print_, decargs +from beets.ui import print_, input_, decargs from beets import autotag import beets.autotag.art from beets import plugins @@ -474,18 +474,17 @@ def manual_search(singleton): """Input either an artist and album (for full albums) or artist and track name (for singletons) for manual search. """ - artist = raw_input('Artist: ').decode(sys.stdin.encoding) - name = raw_input('Track: ' if singleton else 'Album: ') \ - .decode(sys.stdin.encoding) + artist = input_('Artist:') + name = input_('Track:' if singleton else 'Album:') return artist.strip(), name.strip() def manual_id(singleton): """Input a MusicBrainz ID, either for an album ("release") or a track ("recording"). If no valid ID is entered, returns None. """ - prompt = 'Enter MusicBrainz %s ID: ' % \ + prompt = 'Enter MusicBrainz %s ID:' % \ ('recording' if singleton else 'release') - entry = raw_input(prompt).decode(sys.stdin.encoding).strip() + entry = input_(prompt).strip() # Find the first thing that looks like a UUID/MBID. match = re.search('[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}', entry) diff --git a/docs/changelog.rst b/docs/changelog.rst index 42f0052bb..fa48d7422 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -61,6 +61,7 @@ Changelog Mecucci). * Fix an assertion failure while importing with moving enabled when the file was already at its destination. +* Use a nicer error message when input requested but stdin is closed. .. _artist credits: http://wiki.musicbrainz.org/Artist_Credit