From 7e21c684872bea3fc55e2bc29c00a98ee49398e1 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Tue, 20 Jan 2015 16:28:42 -0800 Subject: [PATCH] Logging: pass through non-problematic values 9b83cef was a little too aggressive; we need most objects to go through unconverted so '{0.title}' and such still work in format strings. This is now just a targeted workaround for the case I encountered. --- beets/logging.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/beets/logging.py b/beets/logging.py index bb60761b0..31b12fbb3 100644 --- a/beets/logging.py +++ b/beets/logging.py @@ -24,6 +24,7 @@ from __future__ import absolute_import from copy import copy from logging import * # noqa import sys +import subprocess # We need special hacks for Python 2.6 due to logging.Logger being an @@ -31,8 +32,9 @@ import sys PY26 = sys.version_info[:2] == (2, 6) -def as_unicode(val): - """Coerce a value to Unicode for displaying in a log message. +def logsafe(val): + """Coerce a potentially "problematic" value so it can be formatted + in a Unicode log string. This works around a number of pitfalls when logging objects in Python 2: @@ -42,15 +44,20 @@ def as_unicode(val): `unicode(v)` while `str(v)` works fine. CalledProcessError is an example. """ + # Already Unicode. if isinstance(val, unicode): return val + + # Bytestring: needs decoding. elif isinstance(val, bytes): # Blindly convert with UTF-8. Eventually, it would be nice to # (a) only do this for paths, if they can be given a distinct # type, and (b) warn the developer if they do this for other # bytestrings. return val.decode('utf8', 'replace') - else: + + # A "problem" object: needs a workaround. + elif isinstance(val, subprocess.CalledProcessError): try: return unicode(val) except UnicodeDecodeError: @@ -58,6 +65,11 @@ def as_unicode(val): # instead. return str(val).decode('utf8', 'replace') + # Other objects are used as-is so field access, etc., still works in + # the format string. + else: + return val + class StrFormatLogger(Logger): """A version of `Logger` that uses `str.format`-style formatting @@ -71,8 +83,8 @@ class StrFormatLogger(Logger): self.kwargs = kwargs def __str__(self): - args = [as_unicode(a) for a in self.args] - kwargs = dict((k, as_unicode(v)) for (k, v) in self.kwargs.items()) + args = [logsafe(a) for a in self.args] + kwargs = dict((k, logsafe(v)) for (k, v) in self.kwargs.items()) return self.msg.format(*args, **kwargs) def _log(self, level, msg, args, exc_info=None, extra=None, **kwargs):