plugins: Python 3.10 compatibility

In

766c8b190b from [1]

a slight hack was introduced that allowed to add new arguments to
plugin events without breaking legacy plugins that did not feature
those arguments in the signature of their handler functions.

When improving logging management for plugins in

327b62b610 from [2]

this hack was removed, to be restored with

7f34c101d7 from [3]

Now in addition to inspecting the function signature, an assumption is
made about the string message of the TypeError that occurs for legacy
handler functions (namely, that it start with func.__name__).

In Python 3.10, the TypeError uses func.__qualname__ [4], however, due
to the patch [5]. Thus, checking whether the TypeError is due to an
outdated function signature or something internal to the handler fais.

As a fix, instead of using __name__ or __qualname__ depending on Python
version, let's just revert to the old hack from [1], I'm not seeing a
significant advantage in [3].  The latter might be a bit faster since it
doesn't construct a new dict for each call, but only for legacy calls in
the excption handler. I doubt that really matters, and if it does, we
should try to think of a better fix than inspecting error messages with
no guarantees about their content in such a central place in the code.

[1] https://github.com/beetbox/beets/pull/652
[2] https://github.com/beetbox/beets/pull/1320
[3] https://github.com/beetbox/beets/pull/1359
[4] https://docs.python.org/3.10/glossary.html#term-qualified-name
[5] https://bugs.python.org/issue40679
This commit is contained in:
wisp3rwind 2021-03-22 21:48:30 +01:00
parent fdd1deed22
commit 64dc3ec2a2

View file

@ -130,29 +130,30 @@ class BeetsPlugin(object):
be sent for backwards-compatibility.
"""
if six.PY2:
func_args = inspect.getargspec(func).args
argspec = inspect.getargspec(func)
func_args = argspec.args
has_varkw = argspec.keywords is not None
else:
func_args = inspect.getfullargspec(func).args
argspec = inspect.getfullargspec(func)
func_args = argspec.args
has_varkw = argspec.varkw is not None
@wraps(func)
def wrapper(*args, **kwargs):
assert self._log.level == logging.NOTSET
verbosity = beets.config['verbose'].get(int)
log_level = max(logging.DEBUG, base_log_level - 10 * verbosity)
self._log.setLevel(log_level)
if not has_varkw:
kwargs = dict((k, v) for k, v in kwargs.items()
if k in func_args)
try:
try:
return func(*args, **kwargs)
except TypeError as exc:
if exc.args[0].startswith(func.__name__):
# caused by 'func' and not stuff internal to 'func'
kwargs = dict((arg, val) for arg, val in kwargs.items()
if arg in func_args)
return func(*args, **kwargs)
else:
raise
return func(*args, **kwargs)
finally:
self._log.setLevel(logging.NOTSET)
return wrapper
def queries(self):