napari Plugin Engine v2 (npe2)
npe2 (napari plugin engine v2) is the modern, robust plugin system for napari. It facilitates explicit plugin discovery and contribution management through manifest files and package entry points, aiming for better isolation, explicit dependency management, and improved testability compared to its predecessor. The current version is 0.8.2, and it follows the napari release cycle, with frequent updates during active development phases.
Common errors
-
KeyError: 'Plugin "my-plugin" not found'
cause The specified plugin's manifest could not be discovered or loaded by the `npe2.PluginManager`. This often means the plugin is not correctly installed or its entry points are misconfigured.fix1. Ensure the plugin package is installed (`pip install my-plugin`). 2. Verify the plugin's `pyproject.toml` (or `setup.cfg`) has a correct `[project.entry-points."napari.plugin"]` entry pointing to its `napari.yaml` manifest. 3. Ensure the `napari.yaml` file exists at the specified relative path. -
FileNotFoundError: [Errno 2] No such file or directory: '/path/to/my_plugin/napari.yaml'
cause The path specified in the `napari.plugin` entry point (in `pyproject.toml` or `setup.cfg`) does not match the actual location of the `napari.yaml` manifest file within the installed plugin package.fixCheck the `napari.plugin` entry point configuration to ensure the path is correct relative to the package root, and that the `napari.yaml` file is present in the plugin's source and installed package. -
TypeError: 'str' object is not callable
cause You are likely trying to directly call a string identifier (e.g., a `command_id` or `function_id`) instead of the actual Python callable it represents. `npe2` contributions are often registered as identifiers.fixUse the `PluginManager`'s methods to retrieve and execute the actual callable. For example, use `pm.execute_command("my-plugin.my_command_id", **kwargs)` or `pm.get_function_contributions("my-plugin")` to get the functions.
Warnings
- breaking Migration from npe1 (legacy) to npe2 (modern) requires significant changes for plugin developers. npe2 introduces a manifest-based plugin declaration system, replacing the implicit `napari_` prefixed package discovery of npe1. Plugins developed for npe1 will not be discovered or function correctly under npe2 without migration.
- gotcha npe2 plugin discovery is explicit and relies on correctly configured package entry points and `napari.yaml` manifests. If a plugin's `pyproject.toml` or `setup.cfg` is not correctly configured, or if the `napari.yaml` manifest is missing or malformed, the plugin will not be found by the PluginManager.
- gotcha The `PluginManager.discover()` method has an `include_npe1` argument (default `False` when called directly). If this is not set to `True`, older `npe1` plugins will not be discovered by the standalone `npe2.PluginManager`. Note that `napari`'s internal plugin manager often sets this to `True` for compatibility.
Install
-
pip install npe2
Imports
- PluginManager
from npe2 import PluginManager
- Manifest
from npe2.manifest import Manifest
- napari_plugin_manager
from napari.plugins.manager import napari_plugin_manager
Quickstart
from npe2 import PluginManager
pm = PluginManager.instance() # Get the singleton PluginManager instance
# Discover all plugins, including older npe1 plugins if present
pm.discover(include_npe1=True)
print(f"Discovered {len(pm.plugins)} plugin packages.")
# Example: List contributions from a specific plugin (e.g., 'napari-svg')
# This assumes 'napari-svg' is installed and has contributions
if "napari-svg" in pm.plugins:
print(f"\nContributions from napari-svg:")
for contribution_type in pm.get_plugin_contributions("napari-svg"):
contributions = pm.get_plugin_contributions("napari-svg")[contribution_type]
if contributions:
print(f" {contribution_type}: {list(contributions.keys())}")
else:
print("\nnapari-svg not found or has no contributions. Try another installed plugin.")