sopel.plugins.handlers#

Sopel’s plugin handlers.

New in version 7.0.

Between a plugin (or “module”) and Sopel’s core, Plugin Handlers are used. It is an interface (defined by the AbstractPluginHandler abstract class), that acts as a proxy between Sopel and the plugin, making a clear separation between how the bot behaves and how the plugins work.

From the Sopel class, a plugin must be:

Each subclass of AbstractPluginHandler must implement its methods in order to be used in the application.

At the moment, three types of plugin are handled:

  • PyModulePlugin: manages plugins that can be imported as Python module from a Python package, i.e. where from package import name works

  • PyFilePlugin: manages plugins that are Python files on the filesystem or Python directory (with an __init__.py file inside), that cannot be directly imported and extra steps are necessary

  • EntryPointPlugin: manages plugins that are declared by an entry point; it otherwise behaves like a PyModulePlugin

All expose the same interface and thereby abstract the internal implementation away from the rest of the application.

Important

This is all relatively new. Its usage and documentation is for Sopel core development and advanced developers. It is subject to rapid changes between versions without much (or any) warning.

Do not build your plugin based on what is here, you do not need to.

class sopel.plugins.handlers.AbstractPluginHandler#

Base class for plugin handlers.

This abstract class defines the interface Sopel uses to configure, load, shutdown, etc. a Sopel plugin (or “module”).

It is through this interface that Sopel will interact with its plugins, whether internal (from sopel.builtins) or external (from the Python files in a directory, to sopel_modules.* subpackages).

Sopel’s loader will create a “Plugin Handler” for each plugin it finds, to which it then delegates loading the plugin, listing its functions (commands, jobs, etc.), configuring it, and running any required actions on shutdown (either upon exiting Sopel or unloading that plugin).

abstract configure(settings)#

Configure Sopel’s settings for this plugin.

Parameters:

settings (sopel.config.Config) – Sopel’s configuration

This method will be called by Sopel’s configuration wizard.

abstract get_capability_requests() list[plugin_decorators.capability]#

Retrieve the plugin’s list of capability requests.

abstract get_label() str#

Retrieve a display label for the plugin.

Returns:

a human readable label for display purpose

Return type:

str

This method should, at least, return <module_name> plugin.

abstract get_meta_description() PluginMetaDescription#

Retrieve a meta description for the plugin.

Returns:

Metadata about the plugin

Return type:

dict

The expected keys are detailed in PluginMetaDescription.

abstract get_version()#

Retrieve the plugin’s version.

Returns:

the plugin’s version string

Return type:

str

abstract has_configure() bool#

Tell if the plugin has a configure action.

Returns:

True if the plugin has a configure action, False otherwise

Return type:

bool

abstract has_setup() bool#

Tell if the plugin has a setup action.

Returns:

True if the plugin has a setup, False otherwise

Return type:

bool

abstract has_shutdown() bool#

Tell if the plugin has a shutdown action.

Returns:

True if the plugin has a shutdown action, False otherwise

Return type:

bool

abstract is_loaded() bool#

Tell if the plugin is loaded or not.

Returns:

True if the plugin is loaded, False otherwise

Return type:

bool

This must return True if the load() method has been called with success.

abstract load()#

Load the plugin.

This method must be called first, in order to setup, register, shutdown, or configure the plugin later.

name: str#

Plugin identifier.

The name of a plugin identifies this plugin: when Sopel loads a plugin, it will store its information under that identifier.

abstract register(bot)#

Register the plugin with the bot.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

abstract reload()#

Reload the plugin.

This method can be called once the plugin is already loaded. It will take care of reloading the plugin from its source.

abstract setup(bot)#

Run the plugin’s setup action.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

abstract shutdown(bot)#

Run the plugin’s shutdown action.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

abstract unregister(bot)#

Unregister the plugin from the bot.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

class sopel.plugins.handlers.EntryPointPlugin(entry_point)#

Sopel plugin loaded from an entry point.

Parameters:

entry_point – an entry point object

This handler loads a Sopel plugin exposed by a package’s entry point. It expects to be able to load a module object from the entry point, and to work as a PyModulePlugin from that module.

By default, Sopel searches within the entry point group sopel.plugins. To use that for their own plugins, developers must define an entry point either in their setup.py file or their setup.cfg file:

# in setup.py file
setup(
    name='my_plugin',
    version='1.0',
    entry_points={
        'sopel.plugins': [
            'custom = my_plugin.path.to.plugin',
        ],
    }
)

And this plugin can be loaded with:

>>> from importlib_metadata import entry_points
>>> from sopel.plugins.handlers import EntryPointPlugin
>>> plugin = [
...     EntryPointPlugin(ep)
...     for ep in entry_points(group='sopel.plugins', name='custom')
... ][0]
>>> plugin.load()
>>> plugin.name
'custom'

In this example, the plugin custom is loaded from an entry point. Unlike the PyModulePlugin, the name is not derived from the actual Python module, but from its entry point’s name.

See also

Sopel uses the find_entry_point_plugins() function internally to search entry points.

Entry points are a standard packaging mechanism for Python, used by other applications (such as pytest) for their plugins.

The importlib_metadata backport package is used for consistency across all of Sopel’s supported Python versions. Its API matches that of importlib.metadata from Python 3.10 and up; Sopel will drop this external requirement when practical.

PLUGIN_TYPE = 'setup-entrypoint'#

The plugin’s type.

Metadata for the plugin; this should be considered to be a constant and should not be modified at runtime.

get_meta_description() PluginMetaDescription#

Retrieve a meta description for the plugin.

Returns:

Metadata about the plugin

Return type:

dict

The expected keys are detailed in PluginMetaDescription.

This implementation uses its entry point definition as the source value:

{
    'name': 'example',
    'type': 'setup-entrypoint',
    'label': 'example plugin',
    'source': 'example = my_plugin.example',
    'version': '3.1.2',
}
get_version() str | None#

Retrieve the plugin’s version.

Returns:

the plugin’s version string

Return type:

Optional[str]

load()#

Load the plugin’s module using importlib.import_module().

This method assumes the module is available through sys.path.

name: str#

Plugin identifier.

The name of a plugin identifies this plugin: when Sopel loads a plugin, it will store its information under that identifier.

class sopel.plugins.handlers.PluginMetaDescription(*args, **kwargs)#

Meta description of a plugin, as a dictionary.

This dictionary is expected to contain specific keys:

  • name: a short name for the plugin

  • label: a descriptive label for the plugin; see get_label()

  • type: the plugin’s type

  • source: the plugin’s source (filesystem path, python module/import path, etc.)

  • version: the plugin’s version string if available, otherwise None

class sopel.plugins.handlers.PyFilePlugin(filename)#

Sopel plugin loaded from the filesystem outside of the Python path.

This plugin handler can be used to load a Sopel plugin from the filesystem, either a Python .py file or a directory containing an __init__.py file, and behaves like a PyModulePlugin:

>>> from sopel.plugins.handlers import PyFilePlugin
>>> plugin = PyFilePlugin('/home/sopel/.sopel/plugins/custom.py')
>>> plugin.load()
>>> plugin.name
'custom'

In this example, the plugin custom is loaded from its filename despite not being in the Python path.

PLUGIN_TYPE = 'python-file'#

The plugin’s type.

Metadata for the plugin; this should be considered to be a constant and should not be modified at runtime.

get_meta_description() PluginMetaDescription#

Retrieve a meta description for the plugin.

Returns:

Metadata about the plugin

Return type:

dict

The expected keys are detailed in PluginMetaDescription.

This implementation uses its source file’s path as the source value:

{
    'name': 'example',
    'type': 'python-file',
    'label': 'example plugin',
    'source': '/home/username/.sopel/plugins/example.py',
    'version': '3.1.2',
}
load()#

Load the plugin’s module using importlib.import_module().

This method assumes the module is available through sys.path.

name: str#

Plugin identifier.

The name of a plugin identifies this plugin: when Sopel loads a plugin, it will store its information under that identifier.

reload()#

Reload the plugin.

Unlike PyModulePlugin, it is not possible to use the reload function (either from imp or importlib), because the module might not be available through sys.path.

class sopel.plugins.handlers.PyModulePlugin(name, package=None)#

Sopel plugin loaded from a Python module or package.

A PyModulePlugin represents a Sopel plugin that is a Python module (or package) that can be imported directly.

This:

>>> import sys
>>> from sopel.plugins.handlers import PyModulePlugin
>>> plugin = PyModulePlugin('xkcd', 'sopel.builtins')
>>> plugin.module_name
'sopel.builtins.xkcd'
>>> plugin.load()
>>> plugin.module_name in sys.modules
True

Is the same as this:

>>> import sys
>>> from sopel.builtins import xkcd
>>> 'sopel.builtins.xkcd' in sys.modules
True
PLUGIN_TYPE = 'python-module'#

The plugin’s type.

Metadata for the plugin; this should be considered to be a constant and should not be modified at runtime.

configure(settings)#

Configure Sopel’s settings for this plugin.

Parameters:

settings (sopel.config.Config) – Sopel’s configuration

This method will be called by Sopel’s configuration wizard.

get_capability_requests() list[plugin_decorators.capability]#

Retrieve the plugin’s list of capability requests.

get_label()#

Retrieve a display label for the plugin.

Returns:

a human readable label for display purpose

Return type:

str

By default, this is <name> plugin. If the plugin’s module has a docstring, its first line is used as the plugin’s label.

get_meta_description() PluginMetaDescription#

Retrieve a meta description for the plugin.

Returns:

Metadata about the plugin

Return type:

dict

The expected keys are detailed in PluginMetaDescription.

This implementation uses its module’s dotted import path as the source value:

{
    'name': 'example',
    'type': 'python-module',
    'label': 'example plugin',
    'source': 'sopel_modules.example',
    'version': '3.1.2',
}
get_version() str | None#

Retrieve the plugin’s version.

Returns:

the plugin’s version string

Return type:

Optional[str]

has_configure()#

Tell if the plugin has a configure action.

Returns:

True if the plugin has a configure action, False otherwise

Return type:

bool

The plugin has a configure action if its module has a configure attribute. This attribute is expected to be a callable.

has_setup()#

Tell if the plugin has a setup action.

Returns:

True if the plugin has a setup, False otherwise

Return type:

bool

The plugin has a setup action if its module has a setup attribute. This attribute is expected to be a callable.

has_shutdown()#

Tell if the plugin has a shutdown action.

Returns:

True if the plugin has a shutdown action, False otherwise

Return type:

bool

The plugin has a shutdown action if its module has a shutdown attribute. This attribute is expected to be a callable.

is_loaded()#

Tell if the plugin is loaded or not.

Returns:

True if the plugin is loaded, False otherwise

Return type:

bool

This must return True if the load() method has been called with success.

load()#

Load the plugin’s module using importlib.import_module().

This method assumes the module is available through sys.path.

name: str#

Plugin identifier.

The name of a plugin identifies this plugin: when Sopel loads a plugin, it will store its information under that identifier.

register(bot: Sopel) None#

Register the plugin with the bot.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

reload()#

Reload the plugin’s module using importlib.reload().

This method assumes the plugin is already loaded.

setup(bot)#

Run the plugin’s setup action.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

shutdown(bot)#

Run the plugin’s shutdown action.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel

unregister(bot)#

Unregister the plugin from the bot.

Parameters:

bot (sopel.bot.Sopel) – instance of Sopel