from repobee_plug import _exceptions
from repobee_plug import _corehooks
from repobee_plug import _exthooks
from repobee_plug import _containers
_HOOK_METHODS = {
key: value
for key, value in [
*_exthooks.CloneHook.__dict__.items(),
*_corehooks.PeerReviewHook.__dict__.items(),
*_corehooks.APIHook.__dict__.items(),
*_exthooks.ExtensionCommandHook.__dict__.items(),
*_exthooks.TaskHooks.__dict__.items(),
]
if callable(value) and not key.startswith("_")
}
class _PluginMeta(type):
"""Metaclass used for converting methods with appropriate names into
hook methods.
Also ensures that all public methods have the name of a hook method.
Checking signatures is handled by pluggy on registration.
"""
def __new__(cls, name, bases, attrdict):
"""Check that all public methods have hook names, convert to hook
methods and return a new instance of the class. If there are any
public methods that have non-hook names,
:py:function:`repobee_plug.exception.HookNameError` is raised.
Checking signatures is delegated to ``pluggy`` during registration of
the hook.
"""
methods = cls._extract_public_methods(attrdict)
cls._check_names(methods)
hooked_methods = {
name: _containers.hookimpl(method)
for name, method in methods.items()
}
attrdict.update(hooked_methods)
return super().__new__(cls, name, bases, attrdict)
@staticmethod
def _check_names(methods):
hook_names = set(_HOOK_METHODS.keys())
method_names = set(methods.keys())
if not method_names.issubset(hook_names):
raise _exceptions.HookNameError(
"public method(s) with non-hook name: {}".format(
", ".join(method_names - hook_names)
)
)
@staticmethod
def _extract_public_methods(attrdict):
return {
key: value
for key, value in attrdict.items()
if callable(value) and not key.startswith("_")
}
[docs]class Plugin(metaclass=_PluginMeta):
"""This is a base class for plugin classes. For plugin classes to be picked
up by RepoBee, they must inherit from this class.
Public methods must be hook methods. If there are any public methods that
are not hook methods, an error is raised on creation of the class. As long
as the method has the correct name, it will be recognized as a hook method
during creation. However, if the signature is incorrect, the plugin
framework will raise a runtime exception once it is called. Private methods
(i.e. methods prefixed with ``_``) carry no restrictions.
The signatures of hook methods are not checked until the plugin class is
registered by the :py:const:`repobee_plug.manager` (an instance of
:py:class:`pluggy.manager.PluginManager`). Therefore, when testing a
plugin, it is a good idea to include a test where it is registered with the
manager to ensure that it has the correct signatures.
A plugin class is instantiated exactly once; when RepoBee loads the plugin.
This means that any state that is stored in the plugin will be carried
throughout the execution of a RepoBee command. This makes plugin classes
well suited for implementing tasks that require command line options or
configuration values, as well as for implementing extension commands.
"""