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.

© 2012 - 2023 · Home — Theme Simpleness