diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index 1d6becabe..ff12a1d7b 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -72,14 +72,23 @@ class UserError(Exception): # Utilities. -def _encoding(): - """Tries to guess the encoding used by the terminal.""" +def _out_encoding(): + """Get the encoding to use for *outputting* strings to the console. + """ # Configured override? encoding = config['terminal_encoding'].get() if encoding: return encoding - # Determine from locale settings. + # Python's guessed output stream encoding, or UTF-8 as a fallback + # (e.g., when piped to a file). + return sys.stdout.encoding or 'utf8' + + +def _arg_encoding(): + """Get the encoding for command-line arguments (and other OS + locale-sensitive strings). + """ try: return locale.getdefaultlocale()[1] or 'utf8' except ValueError: @@ -92,7 +101,7 @@ def decargs(arglist): """Given a list of command-line argument bytestrings, attempts to decode them to Unicode strings. """ - return [s.decode(_encoding()) for s in arglist] + return [s.decode(_arg_encoding()) for s in arglist] def print_(*strings): @@ -100,8 +109,8 @@ def print_(*strings): is not in the terminal's encoding's character set, just silently replaces it. - If the arguments are strings then they're expected to share the same type: - either bytes or unicode. + If the arguments are strings then they're expected to share the same + type: either bytes or unicode. """ if strings: if isinstance(strings[0], unicode): @@ -111,7 +120,7 @@ def print_(*strings): else: txt = u'' if isinstance(txt, unicode): - txt = txt.encode(_encoding(), 'replace') + txt = txt.encode(_out_encoding(), 'replace') print(txt) @@ -126,7 +135,7 @@ def input_(prompt=None): # http://bugs.python.org/issue1927 if prompt: if isinstance(prompt, unicode): - prompt = prompt.encode(_encoding(), 'replace') + prompt = prompt.encode(_out_encoding(), 'replace') print(prompt, end=' ') try: diff --git a/docs/changelog.rst b/docs/changelog.rst index 600a80100..06da8f370 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -21,6 +21,9 @@ Little fixes and improvements: * :doc:`/plugins/replaygain`: Fix a number of issues with the new ``bs1770gain`` backend on Windows. Also, fix missing debug output in import mode. :bug:`1398` +* Beets should now be better at guessing the appropriate output encoding on + Windows. (Specifically, the console output encoding is guessed separately + from the encoding for command-line arguments.) :bug:`1419` 1.3.11 (April 5, 2015) ---------------------- diff --git a/test/helper.py b/test/helper.py index 3de31e2df..c23e036e4 100644 --- a/test/helper.py +++ b/test/helper.py @@ -51,7 +51,7 @@ from beets.library import Library, Item, Album from beets import importer from beets.autotag.hooks import AlbumInfo, TrackInfo from beets.mediafile import MediaFile, Image -from beets.ui import _encoding +from beets.ui import _arg_encoding # TODO Move AutotagMock here from test import _common @@ -121,7 +121,7 @@ def has_program(cmd, args=['--version']): full_cmd = [cmd] + args for i, elem in enumerate(full_cmd): if isinstance(elem, unicode): - full_cmd[i] = elem.encode(_encoding()) + full_cmd[i] = elem.encode(_arg_encoding()) try: with open(os.devnull, 'wb') as devnull: subprocess.check_call(full_cmd, stderr=devnull,