File: markers.py

package info (click to toggle)
napari-plugin-engine 0.2.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 432 kB
  • sloc: python: 3,052; makefile: 21
file content (147 lines) | stat: -rw-r--r-- 5,576 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""Hook annotation decorators"""
from typing import Callable, Optional

from .implementation import HookImplementation, HookSpecification


class HookSpecificationMarker:
    """Decorator helper class for marking functions as hook specifications.

    You can instantiate it with a project_name to get a decorator. Calling
    :py:meth:`.PluginManager.add_hookspecs` later will discover all marked
    functions if the :py:class:`.PluginManager` uses the same project_name.
    """

    def __init__(self, project_name):
        self.project_name = project_name

    def __call__(
        self,
        function: Optional[Callable] = None,
        firstresult: bool = False,
        historic: bool = False,
        warn_on_impl=None,
    ):
        """if passed a function, directly sets attributes on the function
        which will make it discoverable to
        :py:meth:`.PluginManager.add_hookspecs`. If passed no function, returns
        a decorator which can be applied to a function later using the
        attributes supplied.

        If ``firstresult`` is ``True`` the 1:N hook call (N being the number of
        registered hook implementation functions) will stop at I<=N when the
        I'th function returns a non-``None`` result.

        If ``historic`` is ``True`` calls to a hook will be memorized and
        replayed on later registered plugins.

        """

        def setattr_hookspec_opts(func):
            if historic and firstresult:
                raise ValueError("cannot have a historic firstresult hook")
            setattr(
                func,
                HookSpecification.format_tag(self.project_name),
                dict(
                    firstresult=firstresult,
                    historic=historic,
                    warn_on_impl=warn_on_impl,
                ),
            )
            return func

        if function is not None:
            return setattr_hookspec_opts(function)
        else:
            return setattr_hookspec_opts


class HookImplementationMarker:
    """Decorator helper class for marking functions as hook implementations.

    You can instantiate with a ``project_name`` to get a decorator. Calling
    :meth:`.PluginManager.register` later will discover all marked functions if
    the :class:`.PluginManager` uses the same project_name.

    Parameters
    ----------
    project_name : str
        A namespace for plugin implementations.  Implementations decorated with
        this class will be discovered by ``PluginManager.register`` if and only
        if ``project_name`` matches the ``project_name`` of the
        ``PluginManager``.
    """

    def __init__(self, project_name: str):
        self.project_name = project_name

    def __call__(
        self,
        function: Optional[Callable] = None,
        *,
        hookwrapper: bool = False,
        optionalhook: bool = False,
        tryfirst: bool = False,
        trylast: bool = False,
        specname: str = '',
    ) -> Callable:
        """Call the marker instance.

        If passed a function, directly sets attributes on the function which
        will make it discoverable to :meth:`.PluginManager.register`. If passed
        no function, returns a decorator which can be applied to a function
        later using the attributes supplied.

        Parameters
        ----------
        function : callable, optional
            A function to decorate as a hook implementation, If ``function`` is
            None, this method returns a function that can be used to decorate
            other functions.
        hookwrapper : bool, optional
            Whether this hook implementation behaves as a hookwrapper.
            by default False
        optionalhook : bool, optional
            If ``True``, a missing matching hook specification will not result
            in an error (by default it is an error if no matching spec is
            found), by default False.
        tryfirst : bool, optional
            If ``True`` this hook implementation will run as early as possible
            in the chain of N hook implementations for a specification, by
            default False
        trylast : bool, optional
            If ``True`` this hook implementation will run as late as possible
            in the chain of N hook implementations, by default False
        specname : str, optional
            If provided, ``specname`` will be used instead of the function name
            when matching this hook implementation to a hook specification
            during registration, by default the implementation function name
            must match the name of the corresponding hook specification.

        Returns
        -------
        Callable
            If ``function`` is not ``None``, will decorate the function with
            attributes, and return the function.  If ``function`` is None, will
            return a decorator that can be used to decorate functions.
        """

        def set_hook_implementation_attributes(func):
            setattr(
                func,
                HookImplementation.format_tag(self.project_name),
                dict(
                    hookwrapper=hookwrapper,
                    optionalhook=optionalhook,
                    tryfirst=tryfirst,
                    trylast=trylast,
                    specname=specname,
                ),
            )
            return func

        if function is None:
            return set_hook_implementation_attributes
        else:
            return set_hook_implementation_attributes(function)