Entry Points#

Packages may provide commands to be run at the console (console scripts), such as the pip command. These commands are defined for a package as a specific kind of entry point in the setup.cfg or setup.py.

Console Scripts#

First consider an example without entry points. Imagine a package defined thus:

timmins/
    timmins/__init__.py
    timmins/__main__.py
    setup.cfg # or setup.py
    #other necessary files

with __init__.py as:

def hello_world():
    print("Hello world")

and __main__.py providing a hook:

from . import hello_world

if __name__ == '__main__':
    hello_world()

After installing the package, the function may be invoked through the runpy module:

python -m timmins

Adding a console script entry point allows the package to define a user-friendly name for installers of the package to execute. Installers like pip will create wrapper scripts to execute a function. In the above example, to create a command hello-world that invokes timmins.hello_world, add a console script entry point to setup.cfg:

[options.entry_points]
console_scripts =
    hello-world = timmins:hello_world

After installing the package, a user may invoke that function by simply calling hello-world on the command line.

The syntax for entry points is specified as follows:

<name> = [<package>.[<subpackage>.]]<module>[:<object>.<object>]

where name is the name for the script you want to create, the left hand side of : is the module that contains your function and the right hand side is the object you want to invoke (e.g. a function).

In addition to console_scripts, Setuptools supports gui_scripts, which will launch a GUI application without running in a terminal window.

Advertising Behavior#

Console scripts are one use of the more general concept of entry points. Entry points more generally allow a packager to advertise behavior for discovery by other libraries and applications. This feature enables “plug-in”-like functionality, where one library solicits entry points and any number of other libraries provide those entry points.

A good example of this plug-in behavior can be seen in pytest plugins, where pytest is a test framework that allows other libraries to extend or modify its functionality through the pytest11 entry point.

The console scripts work similarly, where libraries advertise their commands and tools like pip create wrapper scripts that invoke those commands.

For a project wishing to solicit entry points, Setuptools recommends the importlib.metadata module (part of stdlib since Python 3.8) or its backport, importlib_metadata.

For example, to find the console script entry points from the example above:

>>> from importlib import metadata
>>> eps = metadata.entry_points()['console_scripts']

eps is now a list of EntryPoint objects, one of which corresponds to the hello-world = timmins:hello_world defined above. Each EntryPoint contains the name, group, and value. It also supplies a .load() method to import and load that entry point (module or object).

[options.entry_points]
my.plugins =
    hello-world = timmins:hello_world

Then, a different project wishing to load ‘my.plugins’ plugins could run the following routine to load (and invoke) such plugins:

>>> from importlib import metadata
>>> eps = metadata.entry_points()['my.plugins']
>>> for ep in eps:
...     plugin = ep.load()
...     plugin()
...

The project soliciting the entry points needs not to have any dependency or prior knowledge about the libraries implementing the entry points, and downstream users are able to compose functionality by pulling together libraries implementing the entry points.

Dependency Management#

Some entry points may require additional dependencies to properly function. For such an entry point, declare in square brackets any number of dependency extras following the entry point definition. Such entry points will only be viable if their extras were declared and installed. See the guide on dependencies management for more information on defining extra requirements. Consider from the above example:

[options.entry_points]
console_scripts =
    hello-world = timmins:hello_world [pretty-printer]

In this case, the hello-world script is only viable if the pretty-printer extra is indicated, and so a plugin host might exclude that entry point (i.e. not install a console script) if the relevant extra dependencies are not installed.