mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00

It looks like pyqt5 is playing dirty and checking the number of args of functions before calling them. When using hooks.wrap, pyqt5 thinks it can pass any amount of arguments (because *args) and you get exceptions like this inside the wrap function, when calling the 'old' function: >TypeError: onFindDupes() takes 1 positional argument but 2 were given This commit fixes it by preserving the signature of the wrapped method, by adding an optional dependency on the "decorator" module. Making it an optional dependency is probably not the wisest idea but since this is a small edge case it might be smoother to start like this. I also added functools.wraps() as a fallback, which won't help much but is slightly more correct. See this article for details: https://hynek.me/articles/decorators/
77 lines
2.1 KiB
Python
77 lines
2.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
"""\
|
|
Hooks - hook management and tools for extending Anki
|
|
==============================================================================
|
|
|
|
To find available hooks, grep for runHook and runFilter in the source code.
|
|
|
|
Instrumenting allows you to modify functions that don't have hooks available.
|
|
If you call wrap() with pos='around', the original function will not be called
|
|
automatically but can be called with _old().
|
|
"""
|
|
|
|
import functools
|
|
|
|
try:
|
|
# optional: like functools.wraps, but signature-preserving
|
|
import decorator
|
|
except ImportError:
|
|
decorator = None
|
|
|
|
# Hooks
|
|
##############################################################################
|
|
|
|
_hooks = {}
|
|
|
|
def runHook(hook, *args):
|
|
"Run all functions on hook."
|
|
hook = _hooks.get(hook, None)
|
|
if hook:
|
|
for func in hook:
|
|
func(*args)
|
|
|
|
def runFilter(hook, arg, *args):
|
|
hook = _hooks.get(hook, None)
|
|
if hook:
|
|
for func in hook:
|
|
arg = func(arg, *args)
|
|
return arg
|
|
|
|
def addHook(hook, func):
|
|
"Add a function to hook. Ignore if already on hook."
|
|
if not _hooks.get(hook, None):
|
|
_hooks[hook] = []
|
|
if func not in _hooks[hook]:
|
|
_hooks[hook].append(func)
|
|
|
|
def remHook(hook, func):
|
|
"Remove a function if is on hook."
|
|
hook = _hooks.get(hook, [])
|
|
if func in hook:
|
|
hook.remove(func)
|
|
|
|
# Instrumenting
|
|
##############################################################################
|
|
|
|
def wrap(old, new, pos="after"):
|
|
"Override an existing function."
|
|
def repl(*args, **kwargs):
|
|
if pos == "after":
|
|
old(*args, **kwargs)
|
|
return new(*args, **kwargs)
|
|
elif pos == "before":
|
|
new(*args, **kwargs)
|
|
return old(*args, **kwargs)
|
|
else:
|
|
return new(_old=old, *args, **kwargs)
|
|
|
|
if decorator is None:
|
|
return functools.wraps(repl)
|
|
|
|
def decorator_wrapper(f, *args, **kwargs):
|
|
return repl(*args, **kwargs)
|
|
|
|
return decorator.decorator(decorator_wrapper)(old)
|