6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-11-14 17:24:08 +00:00

feat: filter priorities

Nothing revolutionary here, we just implement the same priority queue that
existed in actions. It will be necessary to trigger init tasks in the right
order.
This commit is contained in:
Régis Behmo 2022-11-08 16:09:16 +01:00 committed by Régis Behmo
parent 7879506910
commit e734f52f07
4 changed files with 59 additions and 32 deletions

View File

@ -8,6 +8,7 @@ will be backported to the master branch at every major release.
When backporting changes to master, we should keep only the entries that correspond to user- When backporting changes to master, we should keep only the entries that correspond to user-
facing changes. facing changes.
--> -->
- [Feature] Implement hook filter priorities, which work like action priorities. (by @regisb)
- 💥[Improvement] Remove the `local/dev bindmount` commands, which have been marked as deprecated for some time. The `--mount` option should be used instead. - 💥[Improvement] Remove the `local/dev bindmount` commands, which have been marked as deprecated for some time. The `--mount` option should be used instead.
- 💥[Bugfix] Fix local installation requirements. Plugins that implemented the "openedx-dockerfile-post-python-requirements" patch and that needed access to the edx-platform repo will no longer work. Instead, these plugins should implement the "openedx-dockerfile-pre-assets" patch. This scenario should be very rare, though. (by @regisb) - 💥[Bugfix] Fix local installation requirements. Plugins that implemented the "openedx-dockerfile-post-python-requirements" patch and that needed access to the edx-platform repo will no longer work. Instead, these plugins should implement the "openedx-dockerfile-pre-assets" patch. This scenario should be very rare, though. (by @regisb)
- 💥[Improvement] Rename the implementation of tutor <mode> quickstart to tutor <mode> launch. (by @Carlos-Muniz) - 💥[Improvement] Rename the implementation of tutor <mode> quickstart to tutor <mode> launch. (by @Carlos-Muniz)

View File

@ -4,14 +4,13 @@ __license__ = "Apache 2.0"
import sys import sys
import typing as t import typing as t
from . import priorities
from .contexts import Contextualized from .contexts import Contextualized
# Similarly to CallableFilter, it should be possible to refine the definition of # Similarly to CallableFilter, it should be possible to refine the definition of
# CallableAction in the future. # CallableAction in the future.
CallableAction = t.Callable[..., None] CallableAction = t.Callable[..., None]
DEFAULT_PRIORITY = 10
class ActionCallback(Contextualized): class ActionCallback(Contextualized):
def __init__( def __init__(
@ -21,7 +20,7 @@ class ActionCallback(Contextualized):
): ):
super().__init__() super().__init__()
self.func = func self.func = func
self.priority = priority or DEFAULT_PRIORITY self.priority = priority or priorities.DEFAULT
def do( def do(
self, *args: t.Any, context: t.Optional[str] = None, **kwargs: t.Any self, *args: t.Any, context: t.Optional[str] = None, **kwargs: t.Any
@ -57,15 +56,7 @@ class Action:
) -> t.Callable[[CallableAction], CallableAction]: ) -> t.Callable[[CallableAction], CallableAction]:
def inner(func: CallableAction) -> CallableAction: def inner(func: CallableAction) -> CallableAction:
callback = ActionCallback(func, priority=priority) callback = ActionCallback(func, priority=priority)
# I wish we could use bisect.insort_right here but the `key=` parameter priorities.insert_callback(callback, self.callbacks)
# is unsupported in Python 3.9
position = 0
while (
position < len(self.callbacks)
and self.callbacks[position].priority <= callback.priority
):
position += 1
self.callbacks.insert(position, callback)
return func return func
return inner return inner
@ -128,8 +119,7 @@ def get_template(name: str) -> ActionTemplate:
def add( def add(
name: str, name: str, priority: t.Optional[int] = None
priority: t.Optional[int] = None,
) -> t.Callable[[CallableAction], CallableAction]: ) -> t.Callable[[CallableAction], CallableAction]:
""" """
Decorator to add a callback action associated to a name. Decorator to add a callback action associated to a name.
@ -139,8 +129,8 @@ def add(
:py:class:`tutor.hooks.Actions` instead. :py:class:`tutor.hooks.Actions` instead.
:param priority: optional order in which the action callbacks are performed. Higher :param priority: optional order in which the action callbacks are performed. Higher
values mean that they will be performed later. The default value is values mean that they will be performed later. The default value is
``DEFAULT_PRIORITY`` (10). Actions that should be performed last should ``priorities.DEFAULT`` (10). Actions that should be performed last should have a
have a priority of 100. priority of 100.
Usage:: Usage::

View File

@ -4,7 +4,7 @@ __license__ = "Apache 2.0"
import sys import sys
import typing as t import typing as t
from . import contexts from . import contexts, priorities
# For now, this signature is not very restrictive. In the future, we could improve it by writing: # For now, this signature is not very restrictive. In the future, we could improve it by writing:
# #
@ -22,9 +22,10 @@ CallableFilter = t.Callable[..., t.Any]
class FilterCallback(contexts.Contextualized): class FilterCallback(contexts.Contextualized):
def __init__(self, func: CallableFilter): def __init__(self, func: CallableFilter, priority: t.Optional[int] = None):
super().__init__() super().__init__()
self.func = func self.func = func
self.priority = priority or priorities.DEFAULT
def apply( def apply(
self, value: T, *args: t.Any, context: t.Optional[str] = None, **kwargs: t.Any self, value: T, *args: t.Any, context: t.Optional[str] = None, **kwargs: t.Any
@ -36,7 +37,7 @@ class FilterCallback(contexts.Contextualized):
class Filter: class Filter:
""" """
Each filter is associated to a name and a list of callbacks. Each filter is associated to a name and a list of callbacks, sorted by priority.
""" """
INDEX: t.Dict[str, "Filter"] = {} INDEX: t.Dict[str, "Filter"] = {}
@ -55,18 +56,21 @@ class Filter:
""" """
return cls.INDEX.setdefault(name, cls(name)) return cls.INDEX.setdefault(name, cls(name))
def add(self) -> t.Callable[[CallableFilter], CallableFilter]: def add(
self, priority: t.Optional[int] = None
) -> t.Callable[[CallableFilter], CallableFilter]:
def inner(func: CallableFilter) -> CallableFilter: def inner(func: CallableFilter) -> CallableFilter:
self.callbacks.append(FilterCallback(func)) callback = FilterCallback(func, priority=priority)
priorities.insert_callback(callback, self.callbacks)
return func return func
return inner return inner
def add_item(self, item: T) -> None: def add_item(self, item: T, priority: t.Optional[int] = None) -> None:
self.add_items([item]) self.add_items([item], priority=priority)
def add_items(self, items: t.List[T]) -> None: def add_items(self, items: t.List[T], priority: t.Optional[int] = None) -> None:
@self.add() @self.add(priority=priority)
def callback(value: t.List[T], *_args: t.Any, **_kwargs: t.Any) -> t.List[T]: def callback(value: t.List[T], *_args: t.Any, **_kwargs: t.Any) -> t.List[T]:
return value + items return value + items
@ -153,11 +157,17 @@ def get_template(name: str) -> FilterTemplate:
return FilterTemplate(name) return FilterTemplate(name)
def add(name: str) -> t.Callable[[CallableFilter], CallableFilter]: def add(
name: str, priority: t.Optional[int] = None
) -> t.Callable[[CallableFilter], CallableFilter]:
""" """
Decorator for functions that will be applied to a single named filter. Decorator for functions that will be applied to a single named filter.
:param name: name of the filter to which the decorated function should be added. :param str name: name of the filter to which the decorated function should be added.
:param int priority: optional order in which the filter callbacks are called. Higher
values mean that they will be performed later. The default value is
``priorities.DEFAULT`` (10). Filters that should be called last should have a
priority of 100.
The return value of each filter function callback will be passed as the first argument to the next one. The return value of each filter function callback will be passed as the first argument to the next one.
@ -174,15 +184,16 @@ def add(name: str) -> t.Callable[[CallableFilter], CallableFilter]:
# After filters have been created, the result of calling all filter callbacks is obtained by running: # After filters have been created, the result of calling all filter callbacks is obtained by running:
hooks.filters.apply("my-filter", initial_value, some_other_argument_value) hooks.filters.apply("my-filter", initial_value, some_other_argument_value)
""" """
return Filter.get(name).add() return Filter.get(name).add(priority=priority)
def add_item(name: str, item: T) -> None: def add_item(name: str, item: T, priority: t.Optional[int] = None) -> None:
""" """
Convenience function to add a single item to a filter that returns a list of items. Convenience function to add a single item to a filter that returns a list of items.
:param name: filter name. :param name: filter name.
:param object item: item that will be appended to the resulting list. :param object item: item that will be appended to the resulting list.
:param int priority: see :py:data:`add`.
Usage:: Usage::
@ -193,10 +204,10 @@ def add_item(name: str, item: T) -> None:
assert ["item1", "item2"] == hooks.filters.apply("my-filter", []) assert ["item1", "item2"] == hooks.filters.apply("my-filter", [])
""" """
get(name).add_item(item) get(name).add_item(item, priority=priority)
def add_items(name: str, items: t.List[T]) -> None: def add_items(name: str, items: t.List[T], priority: t.Optional[int] = None) -> None:
""" """
Convenience function to add multiple item to a filter that returns a list of items. Convenience function to add multiple item to a filter that returns a list of items.
@ -211,7 +222,7 @@ def add_items(name: str, items: t.List[T]) -> None:
assert ["item1", "item2"] == hooks.filters.apply("my-filter", []) assert ["item1", "item2"] == hooks.filters.apply("my-filter", [])
""" """
get(name).add_items(items) get(name).add_items(items, priority=priority)
def iterate( def iterate(

25
tutor/hooks/priorities.py Normal file
View File

@ -0,0 +1,25 @@
import typing as t
from typing_extensions import Protocol
HIGH = 5
DEFAULT = 10
LOW = 50
class PrioritizedCallback(Protocol):
priority: int
TPrioritized = t.TypeVar("TPrioritized", bound=PrioritizedCallback)
def insert_callback(callback: TPrioritized, callbacks: t.List[TPrioritized]) -> None:
# I wish we could use bisect.insort_right here but the `key=` parameter
# is unsupported in Python 3.9
position = 0
while (
position < len(callbacks) and callbacks[position].priority <= callback.priority
):
position += 1
callbacks.insert(position, callback)