2022-02-07 17:11:43 +00:00
|
|
|
# The Tutor plugin system is licensed under the terms of the Apache 2.0 license.
|
|
|
|
__license__ = "Apache 2.0"
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import typing as t
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
from typing_extensions import ParamSpec
|
|
|
|
|
2022-11-08 15:09:16 +00:00
|
|
|
from . import priorities
|
2022-02-07 17:11:43 +00:00
|
|
|
from .contexts import Contextualized
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
P = ParamSpec("P")
|
|
|
|
# Similarly to CallableFilter, it should be possible to create a CallableAction alias in
|
|
|
|
# the future.
|
|
|
|
# CallableAction = t.Callable[P, None]
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
class ActionCallback(Contextualized, t.Generic[P]):
|
2022-02-07 17:11:43 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
func: t.Callable[P, None],
|
2022-02-07 17:11:43 +00:00
|
|
|
priority: t.Optional[int] = None,
|
|
|
|
):
|
|
|
|
super().__init__()
|
|
|
|
self.func = func
|
2022-11-08 15:09:16 +00:00
|
|
|
self.priority = priority or priorities.DEFAULT
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
def do(
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
self,
|
|
|
|
*args: P.args,
|
|
|
|
**kwargs: P.kwargs,
|
2022-02-07 17:11:43 +00:00
|
|
|
) -> None:
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
self.func(*args, **kwargs)
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
class Action(t.Generic[P]):
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
Action hooks have callbacks that are triggered independently from one another.
|
|
|
|
|
|
|
|
Several actions are defined across the codebase. Each action is given a unique name.
|
|
|
|
To each action are associated zero or more callbacks, sorted by priority.
|
|
|
|
|
|
|
|
This is the typical action lifecycle:
|
|
|
|
|
|
|
|
1. Create an action with method :py:meth:`get` (or function :py:func:`get`).
|
|
|
|
2. Add callbacks with method :py:meth:`add` (or function :py:func:`add`).
|
|
|
|
3. Call the action callbacks with method :py:meth:`do` (or function :py:func:`do`).
|
|
|
|
|
|
|
|
The `P` type parameter of the Action class corresponds to the expected signature of
|
|
|
|
the action callbacks. For instance, `Action[[str, int]]` means that the action
|
|
|
|
callbacks are expected to take two arguments: one string and one integer.
|
|
|
|
|
|
|
|
This strong typing makes it easier for plugin developers to quickly check whether they are adding and calling action callbacks correctly.
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
INDEX: t.Dict[str, "Action[t.Any]"] = {}
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
def __init__(self, name: str) -> None:
|
|
|
|
self.name = name
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
self.callbacks: t.List[ActionCallback[P]] = []
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
return f"{self.__class__.__name__}('{self.name}')"
|
|
|
|
|
|
|
|
@classmethod
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
def get(cls, name: str) -> "Action[t.Any]":
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
Get an existing action with the given name from the index, or create one.
|
|
|
|
"""
|
|
|
|
return cls.INDEX.setdefault(name, cls(name))
|
|
|
|
|
|
|
|
def add(
|
|
|
|
self, priority: t.Optional[int] = None
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
) -> t.Callable[[t.Callable[P, None]], t.Callable[P, None]]:
|
|
|
|
"""
|
|
|
|
Add a callback to the action
|
|
|
|
|
|
|
|
This is similar to :py:func:`add`.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def inner(func: t.Callable[P, None]) -> t.Callable[P, None]:
|
2022-02-07 17:11:43 +00:00
|
|
|
callback = ActionCallback(func, priority=priority)
|
2022-11-08 15:09:16 +00:00
|
|
|
priorities.insert_callback(callback, self.callbacks)
|
2022-02-07 17:11:43 +00:00
|
|
|
return func
|
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
def do(
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
self,
|
|
|
|
*args: P.args,
|
|
|
|
**kwargs: P.kwargs,
|
2022-02-07 17:11:43 +00:00
|
|
|
) -> None:
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
"""
|
|
|
|
Run the action callbacks
|
|
|
|
|
|
|
|
This is similar to :py:func:`do`.
|
|
|
|
"""
|
|
|
|
self.do_from_context(None, *args, **kwargs)
|
|
|
|
|
|
|
|
def do_from_context(
|
|
|
|
self,
|
|
|
|
context: t.Optional[str],
|
|
|
|
*args: P.args,
|
|
|
|
**kwargs: P.kwargs,
|
2022-02-07 17:11:43 +00:00
|
|
|
) -> None:
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
"""
|
|
|
|
Same as :py:func:`do` but only run the callbacks from a given context.
|
|
|
|
"""
|
2022-02-07 17:11:43 +00:00
|
|
|
for callback in self.callbacks:
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
if callback.is_in_context(context):
|
|
|
|
try:
|
|
|
|
callback.do(
|
|
|
|
*args,
|
|
|
|
**kwargs,
|
|
|
|
)
|
|
|
|
except:
|
|
|
|
sys.stderr.write(
|
|
|
|
f"Error applying action '{self.name}': func={callback.func} contexts={callback.contexts}'\n"
|
|
|
|
)
|
|
|
|
raise
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
def clear(self, context: t.Optional[str] = None) -> None:
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
"""
|
|
|
|
Clear all or part of the callbacks associated to an action
|
|
|
|
|
|
|
|
This is similar to :py:func:`clear`.
|
|
|
|
"""
|
2022-02-07 17:11:43 +00:00
|
|
|
self.callbacks = [
|
|
|
|
callback
|
|
|
|
for callback in self.callbacks
|
|
|
|
if not callback.is_in_context(context)
|
|
|
|
]
|
|
|
|
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
class ActionTemplate(t.Generic[P]):
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
Action templates are for actions for which the name needs to be formatted
|
|
|
|
before the action can be applied.
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
|
|
|
|
Action templates can generate different :py:class:`Action` objects for which the
|
|
|
|
name matches a certain template.
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, name: str):
|
|
|
|
self.template = name
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
return f"{self.__class__.__name__}('{self.template}')"
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
def __call__(self, *args: t.Any, **kwargs: t.Any) -> Action[P]:
|
2022-02-07 17:11:43 +00:00
|
|
|
return get(self.template.format(*args, **kwargs))
|
|
|
|
|
|
|
|
|
|
|
|
# Syntactic sugar
|
|
|
|
get = Action.get
|
|
|
|
|
|
|
|
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
def get_template(name: str) -> ActionTemplate[t.Any]:
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
Create an action with a template name.
|
|
|
|
|
|
|
|
Templated actions must be formatted with ``(*args)`` before being applied. For example::
|
|
|
|
|
|
|
|
action_template = actions.get_template("namespace:{0}")
|
|
|
|
|
|
|
|
@action_template("name").add()
|
|
|
|
def my_callback():
|
|
|
|
...
|
|
|
|
|
|
|
|
action_template("name").do()
|
|
|
|
"""
|
|
|
|
return ActionTemplate(name)
|
|
|
|
|
|
|
|
|
|
|
|
def add(
|
2022-11-08 15:09:16 +00:00
|
|
|
name: str, priority: t.Optional[int] = None
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
) -> t.Callable[[t.Callable[P, None]], t.Callable[P, None]]:
|
2022-02-07 17:11:43 +00:00
|
|
|
"""
|
|
|
|
Decorator to add a callback action associated to a name.
|
|
|
|
|
|
|
|
:param name: name of the action. For forward compatibility, it is
|
|
|
|
recommended not to hardcode any string here, but to pick a value from
|
|
|
|
:py:class:`tutor.hooks.Actions` instead.
|
|
|
|
:param priority: optional order in which the action callbacks are performed. Higher
|
|
|
|
values mean that they will be performed later. The default value is
|
2022-11-08 15:09:16 +00:00
|
|
|
``priorities.DEFAULT`` (10). Actions that should be performed last should have a
|
|
|
|
priority of 100.
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
Usage::
|
|
|
|
|
|
|
|
from tutor import hooks
|
|
|
|
|
|
|
|
@hooks.actions.add("my-action")
|
|
|
|
def do_stuff():
|
|
|
|
...
|
|
|
|
|
|
|
|
The ``do_stuff`` callback function will be called on ``hooks.actions.do("my-action")``. (see :py:func:`do`)
|
|
|
|
|
|
|
|
The signature of each callback action function must match the signature of the corresponding ``hooks.actions.do`` call. Callback action functions are not supposed to return any value. Returned values will be ignored.
|
|
|
|
"""
|
|
|
|
return get(name).add(priority=priority)
|
|
|
|
|
|
|
|
|
|
|
|
def do(
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
name: str,
|
|
|
|
*args: P.args,
|
|
|
|
**kwargs: P.kwargs,
|
2022-02-07 17:11:43 +00:00
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Run action callbacks associated to a name/context.
|
|
|
|
|
|
|
|
:param name: name of the action for which callbacks will be run.
|
|
|
|
|
|
|
|
Extra ``*args`` and ``*kwargs`` arguments will be passed as-is to
|
|
|
|
callback functions.
|
|
|
|
|
|
|
|
Callbacks are executed in order of priority, then FIFO. There is no error
|
|
|
|
management here: a single exception will cause all following callbacks
|
|
|
|
not to be run and the exception to be bubbled up.
|
|
|
|
"""
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
action: Action[P] = Action.get(name)
|
|
|
|
action.do(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
def do_from_context(
|
|
|
|
context: str,
|
|
|
|
name: str,
|
|
|
|
*args: P.args,
|
|
|
|
**kwargs: P.kwargs,
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Same as :py:func:`do` but only run the callbacks that were created in a given context.
|
|
|
|
|
|
|
|
:param name: name of the action for which callbacks will be run.
|
|
|
|
:param context: limit the set of callback actions to those that
|
|
|
|
were declared within a certain context (see
|
|
|
|
:py:func:`tutor.hooks.contexts.enter`).
|
|
|
|
"""
|
|
|
|
action: Action[P] = Action.get(name)
|
|
|
|
action.do_from_context(context, *args, **kwargs)
|
2022-02-07 17:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def clear_all(context: t.Optional[str] = None) -> None:
|
|
|
|
"""
|
|
|
|
Clear any previously defined filter with the given context.
|
|
|
|
|
|
|
|
This will call :py:func:`clear` with all action names.
|
|
|
|
"""
|
|
|
|
for name in Action.INDEX:
|
|
|
|
clear(name, context=context)
|
|
|
|
|
|
|
|
|
|
|
|
def clear(name: str, context: t.Optional[str] = None) -> None:
|
|
|
|
"""
|
|
|
|
Clear any previously defined action with the given name and context.
|
|
|
|
|
|
|
|
:param name: name of the action callbacks to remove.
|
|
|
|
:param context: when defined, will clear only the actions that were
|
|
|
|
created within that context.
|
|
|
|
|
|
|
|
Actions will be removed from the list of callbacks and will no longer be
|
|
|
|
run in :py:func:`do` calls.
|
|
|
|
|
|
|
|
This function should almost certainly never be called by plugins. It is
|
|
|
|
mostly useful to disable some plugins at runtime or in unit tests.
|
|
|
|
"""
|
feat: strongly typed hooks
Now that the mypy bugs have been resolved, we are able to define more precisely
and cleanly the types of Actions and Filters.
Moreover, can now strongly type named actions and hooks (in consts.py). With
such a strong typing, we get early alerts of hooks called with incorrect
arguments, which is nothing short of awesome :)
This change breaks the hooks API by removing the `context=...` argument. The
reason for that is that we cannot insert arbitrary arguments between `P.args,
P.kwargs`: https://peps.python.org/pep-0612/#the-components-of-a-paramspec
> A function declared as def inner(a: A, b: B, *args: P.args, **kwargs:
> P.kwargs) -> R has type Callable[Concatenate[A, B, P], R]. Placing
> keyword-only parameters between the *args and **kwargs is forbidden.
Getting the documentation to build in nitpicky mode is quite difficult... We
need to add `nitpick_ignore` to the docs conf.py, otherwise sphinx complains
about many missing class references. This, despite upgrading almost all doc
requirements (except docutils).
2022-10-06 10:05:01 +00:00
|
|
|
Action.get(name).clear(context=context)
|