Setuptools Entrypoints Explained
Python's setuptools has a rather interesting feature called "entry points" which facilitates plugin discovery. I'm using this in imhotep to allow linters to live as separate packages.
So how does it work?
In your setup.py
, you defined a property called
entry_points
which is a dictionary mapping the entry point key to a
list of values. The values take the form of .py =
imhotep_pylint.plugin:PyLint
. This is another mapping in string form
from a user-defined string to a dotted module name (and optionally, as
here, an object to import).
from setuptools import setup setup( # ... entry_points={ 'imhotep_linters': [ '.py = imhotep_pylint.plugin:PyLint' ], }, # ... )
Taking an example from imhoteppylint, you can see that this package
defines an entrypoint for "imhoteplinters". This string is part of
the imhotep API and plugins have to register their entrypoints with
that. From there, we define a list of one item, which says that the
.py
string should map to the PyLint
object living in
imhotep_pylint.plugin
.
How do I discover these?
You can use the pkg_resources
package to discover these plugins at
run-time. It looks something like this (pulled from imhotep):
def load_plugins(): tools = [] for ep in pkg_resources.iter_entry_points(group='imhotep_linters'): klass = ep.load() tools.append(klass(run)) return tools
This uses the group name we've mutually agreed upon,
imhotep_linters
, to return an iterable of entrypoints. From there,
we call the load()
method, which will materialize the entrypoint
into a class object. From there, it's just like any other sort of
class which you can instantiate and use.
This is actually all there is to implementing a plugin system for your package. Python's setuptools makes this quite easy! You can find more official docs here.