Example plugin code using importlib.metadata
Using importlib.metadata (introduced in Python 3.8) is the modern, "official" way to handle plugins. Instead of scanning folders, your app asks the Python environment: "Who has registered themselves under my specific group name?"
1. The Plugin Side (plugin_a/pyproject.toml)
The plugin "advertises" itself in its packaging metadata. This is the key step that makes it discoverable.
[project]
name = "my-plugin-a"
version = "0.1.0"
dependencies = ["my-base-app"]
This registers the plugin under a custom group name
[project.entry-points."my_app.plugins"]
hello_plugin = "my_plugin_a.module:HelloPlugin"
2. The Application Side (my_app/main.py)
Your main application uses entry_points() to find and load everything registered under "my_app.plugins".
from importlib.metadata import entry_points
def load_plugins():
# 1. Look up all entry points registered to this group
# Note: in Python 3.10+, you can use entry_points(group='...')
eps = entry_points().select(group='my_app.plugins')
plugins = []
for entry in eps:
print(f"Discovering: {entry.name} from {entry.value}")
# 2. Load the class/function referenced in the metadata
plugin_class = entry.load()
# 3. Instantiate and store
plugins.append(plugin_class())
return plugins
if name == "main":
active_plugins = load_plugins()
for p in active_plugins:
p.run() # Assuming your plugin has a .run() method
Why this is better than folder scanning:
- Zero Path Logic: You don't need to know where the plugin is installed (site-packages, a virtualenv, or a local editable install). If pip knows about it, your app knows about it [1, 2].
- Performance: Python doesn't have to crawl the file system; it just reads the pre-generated metadata files [2].
- Naming Freedom: Plugins can be named anything (e.g., super-cool-addon) but still register themselves under your app's specific namespace [2].
- No init.py Required: It works perfectly with or without namespace packages [1].
Pro-Tip: Testing locally
If you are developing a plugin locally, you must install it in "editable" mode for the entry points to be generated in your environment:
pip install -e ./path/to/plugin_a
Example plugin code using importlib.metadata
Using importlib.metadata (introduced in Python 3.8) is the modern, "official" way to handle plugins. Instead of scanning folders, your app asks the Python environment: "Who has registered themselves under my specific group name?"
1. The Plugin Side (plugin_a/pyproject.toml)
The plugin "advertises" itself in its packaging metadata. This is the key step that makes it discoverable.
[project]
name = "my-plugin-a"
version = "0.1.0"
dependencies = ["my-base-app"]
This registers the plugin under a custom group name
[project.entry-points."my_app.plugins"]
hello_plugin = "my_plugin_a.module:HelloPlugin"
2. The Application Side (my_app/main.py)
Your main application uses entry_points() to find and load everything registered under "my_app.plugins".
from importlib.metadata import entry_points
def load_plugins():
# 1. Look up all entry points registered to this group
# Note: in Python 3.10+, you can use entry_points(group='...')
eps = entry_points().select(group='my_app.plugins')
if name == "main":
active_plugins = load_plugins()
for p in active_plugins:
p.run() # Assuming your plugin has a .run() method
Why this is better than folder scanning:
Pro-Tip: Testing locally
If you are developing a plugin locally, you must install it in "editable" mode for the entry points to be generated in your environment:
pip install -e ./path/to/plugin_a