From 5233e0fcddcba20fb9b79dc650dc137197316791 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 19 Jan 2017 11:31:25 -0800 Subject: [PATCH 1/2] Avoid stdout encoding issues (#2393) --- beets/ui/__init__.py | 17 +++++++++++------ docs/changelog.rst | 3 +++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index ae30a9c60..d69870737 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -126,8 +126,7 @@ def print_(*strings, **kwargs): Python 3. The `end` keyword argument behaves similarly to the built-in `print` - (it defaults to a newline). The value should have the same string - type as the arguments. + (it defaults to a newline). """ if not strings: strings = [u''] @@ -136,11 +135,17 @@ def print_(*strings, **kwargs): txt = u' '.join(strings) txt += kwargs.get('end', u'\n') - # Send bytes to the stdout stream on Python 2. + # Encode the string and write it to stdout. + txt = txt.encode(_out_encoding(), 'replace') if six.PY2: - txt = txt.encode(_out_encoding(), 'replace') - - sys.stdout.write(txt) + # On Python 2, sys.stdout expects bytes. + sys.stdout.write(txt) + else: + # On Python 3, sys.stdout expects text strings and uses the + # exception-throwing encoding error policy. To avoid throwing + # errors and use our configurable encoding override, we use the + # underlying bytes buffer instead. + sys.stdout.buffer.write(txt) # Configuration wrappers. diff --git a/docs/changelog.rst b/docs/changelog.rst index 4fa8aa50f..3b920e51a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -24,6 +24,9 @@ Fixes: * :doc:`/plugins/replaygain`: Fix Python 3 compatibility in the ``bs1770gain`` backend. :bug:`2382` * :doc:`/plugins/bpd`: Report playback times as integer. :bug:`2394` +* On Python 3, the :ref:`terminal_encoding` setting is respected again for + output and printing will no longer crash on systems configured with a + limited encoding. 1.4.3 (January 9, 2017) From a99d5d2ed2081172a3d486a492319cd87e02d141 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 19 Jan 2017 11:42:46 -0800 Subject: [PATCH 2/2] On Python 3 in tests, record `str` output directly This is a little ugly. Eventually, it would be nice to create a single output stream set up with the appropriate encoding settings *at startup* and use that repeatedly, instead of having `print_` check the settings every time. This output stream could be cleanly replaced with a mock harness for testing. Yet another reason to desire a big "beets context" object... --- beets/ui/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index d69870737..df370b52e 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -136,16 +136,22 @@ def print_(*strings, **kwargs): txt += kwargs.get('end', u'\n') # Encode the string and write it to stdout. - txt = txt.encode(_out_encoding(), 'replace') if six.PY2: # On Python 2, sys.stdout expects bytes. - sys.stdout.write(txt) + out = txt.encode(_out_encoding(), 'replace') + sys.stdout.write(out) else: # On Python 3, sys.stdout expects text strings and uses the # exception-throwing encoding error policy. To avoid throwing # errors and use our configurable encoding override, we use the # underlying bytes buffer instead. - sys.stdout.buffer.write(txt) + if hasattr(sys.stdout, 'buffer'): + out = txt.encode(_out_encoding(), 'replace') + sys.stdout.buffer.write(out) + else: + # In our test harnesses (e.g., DummyOut), sys.stdout.buffer + # does not exist. We instead just record the text string. + sys.stdout.write(txt) # Configuration wrappers.