Perhaps as a first step we could allow add-ons to request a specific subscriber execution priority when subscribing to hooks, i.e. something along the lines of:
import bisect
from typing import Callable, List, Tuple
class SortedHook:
_hooks: List[Tuple[int, Callable[[], None]]] = []
def append(self, callback: Callable[[], None], priority: int = 0) -> None:
bisect.insort(self._hooks, (priority, callback))
def remove(self, callback: Callable[[], None]) -> None:
for index, item in enumerate(self._hooks):
if item[1] == callback:
del self._hooks[index]
break
def count(self) -> int:
return len(self._hooks)
def __call__(self) -> None:
for priority, callback in self._hooks:
try:
callback()
except:
# if the hook fails, remove it
self._hooks.remove((priority, callback))
raise
> anki_will_do_something = SortedHook()
> subscriber_that_wants_to_run_early = lambda: print("early")
> subscriber_that_wants_to_run_late = lambda: print("late")
> anki_will_do_something.append(subscriber_that_wants_to_run_late, priority=1)
> anki_will_do_something.append(subscriber_that_wants_to_run_early, priority=-1)
> anki_will_do_something()
early
late
I think that, in general, add-on import order is always a headache to reason about, and having things be predictable is important. However, given that add-ons already make things quite unpredictable by frequently employing tricks like deferring hook subscription or using low-sorted package names, perhaps a canonical API like this would be the lesser evil and actually prevent breakages caused by authors feeling that they need to use hacks (e.g. in case of on_deck_browser_will_render_content
trying to inject HTML between the native stats and other add-ons for add-ons with late import time).
As far as user customizability goes, add-ons that want to support arbitrary execution order, e.g. to influence order of appearance in composite web views, could then expose an option in their settings that allows users to customize the execution priority (I’d imagine not in those terms, but more so e.g. “Show more towards the top/bottom of the screen”).
This would mean that add-on authors would still have full fine-grained control over whether or not the execution order of specific subscribers can be modified, which will reduce the number of cases to test against.
To put it in other terms:
- If we allow users to rename add-on folders (or rather, more easily than they currently can / encourage it as part of the UI), then add-on authors have to account for both variance in the execution order of other add-ons (which they can’t control), and variance in the execution order of their own add-on across all entry points into Anki
- If we instead go with the approach above, then while still exposed to the uncontrollability of other add-ons, authors would have much more control over the execution order of their own code, while also being able to provide some level of fine-grained customizability to their users, if they so desire.
-
Edit: To limit the combinations to think through, we could also approach this step-by-step, and only make some hooks sorted, in particular those involved in composing UIs.
I would say that ideally, though, it would be nice to move to a system like VS Code’s view containers for areas like Anki’s deck browser at some point (or e.g. the editor buttons for that matter), which would allow users to just drag-and-drop UI elements around.