mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00

This adds Python 3.9 and 3.10 typing syntax to files that import attributions from __future___. Python 3.9 should be able to cope with the 3.10 syntax, but Python 3.8 will no longer work. On Windows/Mac, install the latest Python 3.9 version from python.org. There are currently no orjson wheels for Python 3.10 on Windows/Mac, which will break the build unless you have Rust installed separately. On Linux, modern distros should have Python 3.9 available already. If you're on an older distro, you'll need to build Python from source first.
89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
# Copyright: Ankitects Pty Ltd and contributors
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
"""
|
|
Tools for extending Anki.
|
|
|
|
A hook takes a function that does not return a value.
|
|
|
|
A filter takes a function that returns its first argument, optionally
|
|
modifying it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import decorator
|
|
|
|
# You can find the definitions in ../tools/genhooks.py
|
|
from anki.hooks_gen import *
|
|
|
|
# Legacy hook handling
|
|
##############################################################################
|
|
|
|
_hooks: dict[str, list[Callable[..., Any]]] = {}
|
|
|
|
|
|
def runHook(hook: str, *args: Any) -> None:
|
|
"Run all functions on hook."
|
|
hookFuncs = _hooks.get(hook, None)
|
|
if hookFuncs:
|
|
for func in hookFuncs:
|
|
try:
|
|
func(*args)
|
|
except:
|
|
hookFuncs.remove(func)
|
|
raise
|
|
|
|
|
|
def runFilter(hook: str, arg: Any, *args: Any) -> Any:
|
|
hookFuncs = _hooks.get(hook, None)
|
|
if hookFuncs:
|
|
for func in hookFuncs:
|
|
try:
|
|
arg = func(arg, *args)
|
|
except:
|
|
hookFuncs.remove(func)
|
|
raise
|
|
return arg
|
|
|
|
|
|
def addHook(hook: str, func: Callable) -> None:
|
|
"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: Any, func: Any) -> None:
|
|
"Remove a function if is on hook."
|
|
hook = _hooks.get(hook, [])
|
|
if func in hook:
|
|
hook.remove(func)
|
|
|
|
|
|
# Monkey patching
|
|
##############################################################################
|
|
# Please only use this for prototyping or for when hooks are not practical,
|
|
# as add-ons that use monkey patching are more likely to break when Anki is
|
|
# updated.
|
|
#
|
|
# If you call wrap() with pos='around', the original function will not be called
|
|
# automatically but can be called with _old().
|
|
def wrap(old: Any, new: Any, pos: str = "after") -> Callable:
|
|
"Override an existing function."
|
|
|
|
def repl(*args: Any, **kwargs: Any) -> Any:
|
|
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)
|
|
|
|
def decorator_wrapper(f: Any, *args: Any, **kwargs: Any) -> Any:
|
|
return repl(*args, **kwargs)
|
|
|
|
return decorator.decorator(decorator_wrapper)(old)
|