mirror of
https://github.com/rembo10/headphones.git
synced 2026-07-21 00:14:02 +01:00
update Beets
This commit is contained in:
+59
-47
@@ -12,63 +12,50 @@
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
"""A drop-in replacement for the standard-library `logging` module that
|
||||
allows {}-style log formatting on Python 2 and 3.
|
||||
"""A drop-in replacement for the standard-library `logging` module.
|
||||
|
||||
Provides everything the "logging" module does. The only difference is
|
||||
that when getLogger(name) instantiates a logger that logger uses
|
||||
{}-style formatting.
|
||||
Provides everything the "logging" module does. In addition, beets' logger
|
||||
(as obtained by `getLogger(name)`) supports thread-local levels, and messages
|
||||
use {}-style formatting and can interpolate keywords arguments to the logging
|
||||
calls (`debug`, `info`, etc).
|
||||
"""
|
||||
|
||||
|
||||
from copy import copy
|
||||
from logging import * # noqa
|
||||
import subprocess
|
||||
import logging
|
||||
import threading
|
||||
from copy import copy
|
||||
|
||||
|
||||
def logsafe(val):
|
||||
"""Coerce a potentially "problematic" value so it can be formatted
|
||||
in a Unicode log string.
|
||||
"""Coerce `bytes` to `str` to avoid crashes solely due to logging.
|
||||
|
||||
This works around a number of pitfalls when logging objects in
|
||||
Python 2:
|
||||
- Logging path names, which must be byte strings, requires
|
||||
conversion for output.
|
||||
- Some objects, including some exceptions, will crash when you call
|
||||
`unicode(v)` while `str(v)` works fine. CalledProcessError is an
|
||||
example.
|
||||
This is particularly relevant for bytestring paths. Much of our code
|
||||
explicitly uses `displayable_path` for them, but better be safe and prevent
|
||||
any crashes that are solely due to log formatting.
|
||||
"""
|
||||
# Already Unicode.
|
||||
if isinstance(val, str):
|
||||
return val
|
||||
|
||||
# Bytestring: needs decoding.
|
||||
elif isinstance(val, bytes):
|
||||
# Bytestring: Needs decoding to be safe for substitution in format strings.
|
||||
if 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('utf-8', 'replace')
|
||||
|
||||
# A "problem" object: needs a workaround.
|
||||
elif isinstance(val, subprocess.CalledProcessError):
|
||||
try:
|
||||
return str(val)
|
||||
except UnicodeDecodeError:
|
||||
# An object with a broken __unicode__ formatter. Use __str__
|
||||
# instead.
|
||||
return str(val).decode('utf-8', 'replace')
|
||||
return val.decode("utf-8", "replace")
|
||||
|
||||
# Other objects are used as-is so field access, etc., still works in
|
||||
# the format string.
|
||||
else:
|
||||
return val
|
||||
# the format string. Relies on a working __str__ implementation.
|
||||
return val
|
||||
|
||||
|
||||
class StrFormatLogger(Logger):
|
||||
class StrFormatLogger(logging.Logger):
|
||||
"""A version of `Logger` that uses `str.format`-style formatting
|
||||
instead of %-style formatting.
|
||||
instead of %-style formatting and supports keyword arguments.
|
||||
|
||||
We cannot easily get rid of this even in the Python 3 era: This custom
|
||||
formatting supports substitution from `kwargs` into the message, which the
|
||||
default `logging.Logger._log()` implementation does not.
|
||||
|
||||
Remark by @sampsyo: https://stackoverflow.com/a/24683360 might be a way to
|
||||
achieve this with less code.
|
||||
"""
|
||||
|
||||
class _LogMessage:
|
||||
@@ -82,19 +69,39 @@ class StrFormatLogger(Logger):
|
||||
kwargs = {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):
|
||||
def _log(
|
||||
self,
|
||||
level,
|
||||
msg,
|
||||
args,
|
||||
exc_info=None,
|
||||
extra=None,
|
||||
stack_info=False,
|
||||
**kwargs,
|
||||
):
|
||||
"""Log msg.format(*args, **kwargs)"""
|
||||
m = self._LogMessage(msg, args, kwargs)
|
||||
return super()._log(level, m, (), exc_info, extra)
|
||||
|
||||
stacklevel = kwargs.pop("stacklevel", 1)
|
||||
stacklevel = {"stacklevel": stacklevel}
|
||||
|
||||
return super()._log(
|
||||
level,
|
||||
m,
|
||||
(),
|
||||
exc_info=exc_info,
|
||||
extra=extra,
|
||||
stack_info=stack_info,
|
||||
**stacklevel,
|
||||
)
|
||||
|
||||
|
||||
class ThreadLocalLevelLogger(Logger):
|
||||
"""A version of `Logger` whose level is thread-local instead of shared.
|
||||
"""
|
||||
class ThreadLocalLevelLogger(logging.Logger):
|
||||
"""A version of `Logger` whose level is thread-local instead of shared."""
|
||||
|
||||
def __init__(self, name, level=NOTSET):
|
||||
def __init__(self, name, level=logging.NOTSET):
|
||||
self._thread_level = threading.local()
|
||||
self.default_level = NOTSET
|
||||
self.default_level = logging.NOTSET
|
||||
super().__init__(name, level)
|
||||
|
||||
@property
|
||||
@@ -121,12 +128,17 @@ class BeetsLogger(ThreadLocalLevelLogger, StrFormatLogger):
|
||||
pass
|
||||
|
||||
|
||||
my_manager = copy(Logger.manager)
|
||||
my_manager = copy(logging.Logger.manager)
|
||||
my_manager.loggerClass = BeetsLogger
|
||||
|
||||
|
||||
# Act like the stdlib logging module by re-exporting its namespace.
|
||||
from logging import * # noqa
|
||||
|
||||
|
||||
# Override the `getLogger` to use our machinery.
|
||||
def getLogger(name=None): # noqa
|
||||
if name:
|
||||
return my_manager.getLogger(name)
|
||||
else:
|
||||
return Logger.root
|
||||
return logging.Logger.root
|
||||
|
||||
Reference in New Issue
Block a user