120 lines
3.3 KiB
Python
120 lines
3.3 KiB
Python
import abc
|
|
from typing import ClassVar, ContextManager, List, Optional
|
|
|
|
import click
|
|
|
|
from pyhanko.cli._ctx import CLIContext
|
|
from pyhanko.sign import Signer
|
|
|
|
__all__ = [
|
|
'SigningCommandPlugin',
|
|
'register_signing_plugin',
|
|
'CLIContext',
|
|
'SIGNING_PLUGIN_REGISTRY',
|
|
'SIGNING_PLUGIN_ENTRY_POINT_GROUP',
|
|
]
|
|
|
|
|
|
class SigningCommandPlugin(abc.ABC):
|
|
"""
|
|
|
|
.. versionadded:: 0.18.0
|
|
|
|
Interface for integrating custom, user-supplied
|
|
signers into the pyHanko CLI as subcommands of ``addsig``.
|
|
|
|
Implementations are discovered through the ``pyhanko.cli_plugin.signing``
|
|
package entry point. Such entry points can be registered in
|
|
``pyproject.toml`` as follows:
|
|
|
|
.. code-block:: toml
|
|
|
|
[project.entry-points."pyhanko.cli_plugin.signing"]
|
|
your_plugin = "some_package.path.to.module:SomePluginClass"
|
|
|
|
Subclasses exposed as entry points are required to have a no-arguments
|
|
``__init__`` method.
|
|
|
|
.. warning::
|
|
This is an incubating feature. API adjustments are still possible.
|
|
|
|
.. warning::
|
|
Plugin support requires Python 3.8 or later.
|
|
"""
|
|
|
|
subcommand_name: ClassVar[str]
|
|
"""
|
|
The name of the subcommand for the plugin.
|
|
"""
|
|
|
|
help_summary: ClassVar[str]
|
|
"""
|
|
A short description of the plugin for use in the ``--help`` output.
|
|
"""
|
|
|
|
unavailable_message: ClassVar[Optional[str]] = None
|
|
"""
|
|
Message to display if the plugin is unavailable.
|
|
"""
|
|
|
|
def click_options(self) -> List[click.Option]:
|
|
"""
|
|
The list of ``click`` options for your custom command.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def click_extra_arguments(self) -> List[click.Argument]:
|
|
"""
|
|
The list of ``click`` arguments for your custom command.
|
|
"""
|
|
return []
|
|
|
|
def is_available(self) -> bool:
|
|
"""
|
|
A hook to determine whether your plugin is available
|
|
or not (e.g. based on the availability of certain dependencies).
|
|
This should not depend on the pyHanko configuration, but may query
|
|
system information in other ways as appropriate.
|
|
|
|
The default is to always report the plugin as available.
|
|
|
|
:return:
|
|
return ``True`` if the plugin is available, else ``False``
|
|
"""
|
|
return True
|
|
|
|
def create_signer(
|
|
self, context: CLIContext, **kwargs
|
|
) -> ContextManager[Signer]:
|
|
"""
|
|
Instantiate a context manager that creates and potentially
|
|
also implements a deallocator for a :class:`.Signer` object.
|
|
|
|
:param context:
|
|
The active :class:`.CLIContext`.
|
|
:param kwargs:
|
|
All keyword arguments processed by ``click`` through the CLI,
|
|
resulting from :meth:`click_options` and
|
|
:meth:`click_extra_arguments`.
|
|
:return:
|
|
A context manager that manages the lifecycle for a :class:`.Signer`.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
SIGNING_PLUGIN_REGISTRY: List[SigningCommandPlugin] = []
|
|
SIGNING_PLUGIN_ENTRY_POINT_GROUP = 'pyhanko.cli_plugin.signing'
|
|
|
|
|
|
def register_signing_plugin(cls):
|
|
"""
|
|
Manually put a plugin into the signing plugin registry.
|
|
|
|
:param cls:
|
|
A plugin class.
|
|
:return:
|
|
The same class.
|
|
"""
|
|
SIGNING_PLUGIN_REGISTRY.append(cls())
|
|
return cls
|