1.. _`entry_points`:
2
3============
4Entry Points
5============
6
7Packages may provide commands to be run at the console (console scripts),
8such as the ``pip`` command. These commands are defined for a package
9as a specific kind of entry point in the ``setup.cfg`` or
10``setup.py``.
11
12
13Console Scripts
14===============
15
16First consider an example without entry points. Imagine a package
17defined thus:
18
19.. code-block:: bash
20
21    timmins/
22        timmins/__init__.py
23        timmins/__main__.py
24        setup.cfg # or setup.py
25        #other necessary files
26
27with ``__init__.py`` as:
28
29.. code-block:: python
30
31    def hello_world():
32        print("Hello world")
33
34and ``__main__.py`` providing a hook:
35
36.. code-block:: python
37
38    from . import hello_world
39
40    if __name__ == '__main__':
41        hello_world()
42
43After installing the package, the function may be invoked through the
44`runpy <https://docs.python.org/3/library/runpy.html>`_ module:
45
46.. code-block:: bash
47
48    python -m timmins
49
50Adding a console script entry point allows the package to define a
51user-friendly name for installers of the package to execute. Installers
52like pip will create wrapper scripts to execute a function. In the
53above example, to create a command ``hello-world`` that invokes
54``timmins.hello_world``, add a console script entry point to
55``setup.cfg``:
56
57.. tab:: setup.cfg
58
59	.. code-block:: ini
60
61		[options.entry_points]
62		console_scripts =
63			hello-world = timmins:hello_world
64
65.. tab:: setup.py
66
67    .. code-block:: python
68
69        from setuptools import setup
70
71        setup(
72            name='timmins',
73            version='0.0.1',
74            packages=['timmins'],
75			# ...
76            entry_points={
77				'console_scripts': [
78					'hello-world=timmins:hello_world',
79				]
80			}
81        )
82
83
84After installing the package, a user may invoke that function by simply calling
85``hello-world`` on the command line.
86
87The syntax for entry points is specified as follows:
88
89.. code-block:: ini
90
91    <name> = [<package>.[<subpackage>.]]<module>[:<object>.<object>]
92
93where ``name`` is the name for the script you want to create, the left hand
94side of ``:`` is the module that contains your function and the right hand
95side is the object you want to invoke (e.g. a function).
96
97In addition to ``console_scripts``, Setuptools supports ``gui_scripts``, which
98will launch a GUI application without running in a terminal window.
99
100
101.. _dynamic discovery of services and plugins:
102
103Advertising Behavior
104====================
105
106Console scripts are one use of the more general concept of entry points. Entry
107points more generally allow a packager to advertise behavior for discovery by
108other libraries and applications. This feature enables "plug-in"-like
109functionality, where one library solicits entry points and any number of other
110libraries provide those entry points.
111
112A good example of this plug-in behavior can be seen in
113`pytest plugins <https://docs.pytest.org/en/latest/writing_plugins.html>`_,
114where pytest is a test framework that allows other libraries to extend
115or modify its functionality through the ``pytest11`` entry point.
116
117The console scripts work similarly, where libraries advertise their commands
118and tools like ``pip`` create wrapper scripts that invoke those commands.
119
120For a project wishing to solicit entry points, Setuptools recommends the
121`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
122module (part of stdlib since Python 3.8) or its backport,
123:pypi:`importlib_metadata`.
124
125For example, to find the console script entry points from the example above:
126
127.. code-block:: pycon
128
129    >>> from importlib import metadata
130    >>> eps = metadata.entry_points()['console_scripts']
131
132``eps`` is now a list of ``EntryPoint`` objects, one of which corresponds
133to the ``hello-world = timmins:hello_world`` defined above. Each ``EntryPoint``
134contains the ``name``, ``group``, and ``value``. It also supplies a ``.load()``
135method to import and load that entry point (module or object).
136
137.. code-block:: ini
138
139    [options.entry_points]
140    my.plugins =
141        hello-world = timmins:hello_world
142
143Then, a different project wishing to load 'my.plugins' plugins could run
144the following routine to load (and invoke) such plugins:
145
146.. code-block:: pycon
147
148    >>> from importlib import metadata
149    >>> eps = metadata.entry_points()['my.plugins']
150    >>> for ep in eps:
151    ...     plugin = ep.load()
152    ...     plugin()
153    ...
154
155The project soliciting the entry points needs not to have any dependency
156or prior knowledge about the libraries implementing the entry points, and
157downstream users are able to compose functionality by pulling together
158libraries implementing the entry points.
159
160
161Dependency Management
162=====================
163
164Some entry points may require additional dependencies to properly function.
165For such an entry point, declare in square brackets any number of dependency
166``extras`` following the entry point definition. Such entry points will only
167be viable if their extras were declared and installed. See the
168:doc:`guide on dependencies management <dependency_management>` for
169more information on defining extra requirements. Consider from the
170above example:
171
172.. code-block:: ini
173
174    [options.entry_points]
175    console_scripts =
176        hello-world = timmins:hello_world [pretty-printer]
177
178In this case, the ``hello-world`` script is only viable if the ``pretty-printer``
179extra is indicated, and so a plugin host might exclude that entry point
180(i.e. not install a console script) if the relevant extra dependencies are not
181installed.
182