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:
parent
7879506910
commit
e734f52f07
@ -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)
|
||||||
|
@ -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::
|
||||||
|
|
||||||
|
@ -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
25
tutor/hooks/priorities.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user