Python Plugin: Entry Points
I’m keen on exploring the architecture of Python plugins. It appears that the first step is to understand Entry Points. In this post, I will describe my own detailed implementation of the examples presented in the aforementioned documentation.
Python Plugin: Entry Points |
The official documentation briefly mentions what to do, but it does not contain any precise instructions on how to do the mentioned task. I had to guess. It turns out my guess was correct, so I want to write it down for future reference.
💥 Please note the following. ⓵ In the aforementioned official documentation,
the timmins-plugin-fancy
plugin does not know anything about
the timmins
application. To further my own understanding, I modified
the timmins-plugin-fancy
plugin in my study to have access to the
timmins
application’s “API”. ⓶ I also added a command of
my own into the timmins
application.
Below are the final directory structures of the two projects.
▶️Windows 10: F:\timmins\
▶️Ubuntu 22.10: /home/behai/timmins
.
├── pyproject.toml
├── src
│ └── timmins
│ ├── bonjour.py
│ ├── hello_world_gui.py
│ ├── __init__.py
│ └── __main__.py
└── venv/
❷ The timmins-plugin-fancy
plugin:
▶️Windows 10: F:\timmins_plugin_fancy\
▶️Ubuntu 22.10: /home/behai/timmins_plugin_fancy
.
├── pyproject.toml
├── src
│ └── timmins_plugin_fancy
│ └── __init__.py
└── venv/
I followed the documentation step by step, incorporating my own code at the appropriate stages. Below, I am providing the final version of the content for the files in each project.
⓵ Content of /home/behai/timmins/pyproject.toml:
[build-system]
requires = ["setuptools>=69.5.1", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "timmins"
version = "0.0.0"
description = "Learning Entry Point."
authors = [{ name = "Van Be Hai Nguyen", email = "behai_nguyen@hotmail.com" }]
dependencies = [
'tomli; python_version >= "3.12"',
'PySimpleGUI',
]
requires-python = ">=3.10"
[project.optional-dependencies]
build = [
'build',
]
[project.scripts]
hello-world = "timmins:hello_world"
ca-va = "timmins.bonjour:ca_va"
[project.gui-scripts]
hello-world-gui = "timmins.hello_world_gui:hello_world"
● We need the build package to create the distribution wheel file
timmins-0.0.0-py3-none-any.whl
.
● The ca-va
command is my own code, added to enhance my understanding
of how project.scripts
works.
● The hello-world-gui
is from the section
GUI Scripts. Instead of replacing
the hello_world()
console script, I use a separate module
hello_world_gui.py
and this new command.
⓶ Content of /home/behai/timmins/src/timmins/__init__.py:
from importlib.metadata import entry_points
display_eps = entry_points(group='timmins.display')
"""
try:
display = display_eps[0].load()
except IndexError:
def display(text):
print(text)
def hello_world():
display('Hello world')
"""
# OR
"""
try:
display = display_eps['lined'].load()
except KeyError:
def display(text):
print(text)
def hello_world():
display('Hello world')
"""
def hello_world():
for ep in display_eps:
display = ep.load()
display('Hello world')
def plugin_api():
print("This is the timmins application plugin_api()")
Please note, the commented out codes are the prior steps discussed in the documentation which I carried out. They are all valid code. Everything should be self-explanatory.
👉 Please pay attention to the method plugin_api()
. This is my own
code, added to enhance my understanding of plugins. The timmins-plugin-fancy
plugin will call this method.
To call this method, any plugin at all will have to install the
timmins-0.0.0-py3-none-any.whl
application package,
which we will discuss in a later section.
⓷ Content of /home/behai/timmins/src/timmins/__main__.py:
from . import hello_world
if __name__ == '__main__':
hello_world()
The content of this module is exactly as in the documentation.
⓸ Content of /home/behai/timmins/src/timmins/bonjour.py:
def ca_va():
print("Bonjour, comment ça va?")
This module implements the ca-va
command.
⓹ Content of /home/behai/timmins/src/timmins/hello_world_gui.py:
import PySimpleGUI as sg
def hello_world():
sg.Window(title="Hello world", layout=[[]], margins=(100, 50)).read()
As mentioned previously, this module is from the section GUI Scripts.
If you haven’t used the PySimpleGUI package before, you’ll need to register as a hobbyist to obtain a free product key.
❷ The timmins-plugin-fancy
plugin:
⓵ Content of /home/behai/timmins_plugin_fancy/pyproject.toml:
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "timmins-plugin-fancy"
version = "0.0.3"
description = "timmins-plugin-fancy"
authors = [
{name = "Van Be Hai Nguyen", email = "behai_nguyen@hotmail.com"}
]
maintainers = [
{name = "Van Be Hai Nguyen", email = "behai_nguyen@hotmail.com"}
]
dependencies = [
'tomli; python_version >= "3.10"',
]
requires-python = ">=3.10"
[project.optional-dependencies]
build = [
"build",
]
# Note the quotes around timmins.display in order to escape the dot .
[project.entry-points."timmins.display"]
excl = "timmins_plugin_fancy:excl_display"
lined = "timmins_plugin_fancy:lined_display"
⓶ Content of /home/behai/timmins_plugin_fancy/src/timmins_plugin_fancy/__init__.py:
from timmins import plugin_api
def excl_display(text):
plugin_api()
print('!!!', text, '!!!')
def lined_display(text):
plugin_api()
print(''.join(['-' for _ in text]))
print(text)
print(''.join(['-' for _ in text]))
👉 Please note the import statement and the calls to the method
plugin_api(). Apart from the
plugin_api()
method, the code in this module is identical
to that in the documentation.
We will now describe the steps to set up and to run the examples. Recall the following:
● On Windows 10, the timmins
application is in the F:\timmins\
directory, on Ubuntu 22.10 it is /home/behai/timmins/
.
● On Windows 10, the timmins-plugin-fancy
plugin is in the
F:\timmins_plugin_fancy\
directory, on Ubuntu 22.10 it is
/home/behai/timmins_plugin_fancy/
.
❶ Prepare and build the timmins
application:
Create the virtual environment venv
:
▶️Windows 10: F:\timmins>C:\PF\Python312\python.exe -m venv venv
▶️Ubuntu 22.10: behai@hp-pavilion-15:~/timmins$ /usr/local/bin/python3.12 -m venv venv
Activate the virtual environment venv
:
▶️Windows 10: F:\timmins>venv\Scripts\activate
▶️Ubuntu 22.10: behai@hp-pavilion-15:~/timmins$ source ./venv/bin/activate
Editable install the required dependencies:
▶️Windows 10: (venv) F:\timmins>venv\Scripts\pip.exe install -e .
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins$ ./venv/bin/pip install -e .
Editable install the optional build
dependencies:
▶️Windows 10: (venv) F:\timmins>venv\Scripts\pip.exe install -e .[build]
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins$ ./venv/bin/pip install -e .[build]
Create the distribution wheel file timmins-0.0.0-py3-none-any.whl
:
▶️Windows 10: (venv) F:\timmins>venv\Scripts\python.exe -m build
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins$ ./venv/bin/python -m build
A new sub-directory ./dist
is created, the wheel file
timmins-0.0.0-py3-none-any.whl
is in there, together with
timmins-0.0.0.tar.gz
.
👉 Copy the timmins-0.0.0-py3-none-any.whl
file to the root
directory of the timmins-plugin-fancy
plugin, which is
F:\timmins_plugin_fancy\
on Windows 10 and
/home/behai/timmins_plugin_fancy/
on Ubuntu 22.10.
❷ Prepare and build the timmins-plugin-fancy
plugin:
Create the virtual environment venv
:
▶️Windows 10: F:\timmins_plugin_fancy>C:\PF\Python312\python.exe -m venv venv
▶️Ubuntu 22.10: behai@hp-pavilion-15:~/timmins_plugin_fancy$ /usr/local/bin/python3.12 -m venv venv
Activate the virtual environment venv
:
▶️Windows 10: F:\timmins_plugin_fancy>venv\Scripts\activate
▶️Ubuntu 22.10: behai@hp-pavilion-15:~/timmins_plugin_fancy$ source ./venv/bin/activate
Editable install the required dependencies:
▶️Windows 10: (venv) F:\timmins_plugin_fancy>venv\Scripts\pip.exe install -e .
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins_plugin_fancy$ ./venv/bin/pip install -e .
Install the timmins
application wheel file,
timmins-0.0.0-py3-none-any.whl
, which was created
and copied over in the previous step:
▶️Windows 10: (venv) F:\timmins_plugin_fancy>venv\Scripts\pip.exe install timmins-0.0.0-py3-none-any.whl
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins_plugin_fancy$ ./venv/bin/pip install timmins-0.0.0-py3-none-any.whl
Editable install the optional build
dependencies:
▶️Windows 10: (venv) F:\timmins_plugin_fancy>venv\Scripts\pip.exe install -e .[build]
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins_plugin_fancy$ ./venv/bin/pip install -e .[build]
Create the distribution wheel file timmins_plugin_fancy-0.0.3-py3-none-any.whl
:
▶️Windows 10: (venv) F:\timmins_plugin_fancy>venv\Scripts\python.exe -m build
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins_plugin_fancy$ ./venv/bin/python -m build
A new sub-directory ./dist
is created, the wheel file
timmins_plugin_fancy-0.0.3-py3-none-any.whl
is in there, together with
timmins_plugin_fancy-0.0.3.tar.gz
.
👉 Copy the timmins_plugin_fancy-0.0.3-py3-none-any.whl
to
the timmins
application, which is
F:\timmins\
on Windows 10 and /home/behai/timmins/
on Ubuntu 22.10.
❸ Running the timmins
application:
Install the timmins-plugin-fancy
plugin wheel file,
timmins_plugin_fancy-0.0.3-py3-none-any.whl
, which was
created and copied over in the previous step:
▶️Windows 10: (venv) F:\timmins>venv\Scripts\pip.exe install timmins_plugin_fancy-0.0.3-py3-none-any.whl
▶️Ubuntu 22.10: (venv) behai@hp-pavilion-15:~/timmins$ ./venv/bin/pip install timmins_plugin_fancy-0.0.3-py3-none-any.whl
We are now ready to run all commands implemented by the application. They are:
▶️Windows 10:
(venv) F:\timmins>venv\Scripts\python -m timmins
(venv) F:\timmins>hello-world
(venv) F:\timmins>ca-va
(venv) F:\timmins>hello-world-gui
▶️Ubuntu 22.10:
(venv) behai@hp-pavilion-15:~/timmins$ ./venv/bin/python -m timmins
(venv) behai@hp-pavilion-15:~/timmins$ hello-world
(venv) behai@hp-pavilion-15:~/timmins$ ca-va
The screenshots below illustrate the responses from each command. I hope they make sense to you:
I didn’t run the hello-world-gui
command on Ubuntu 22.10 because
my laptop is in another room. I was running an SSH
session and
didn’t feel like fetching the laptop. 😂
This is the first time I’ve delved into this area of Python. I hope to build a system that can extend its functionalities through plugins. I believe this is the first step towards building such a system.
I am not sure if the approach I have described in this post is the best to fully implement the examples. Please treat it as my learning attempts only, and keep an open mind that there can be better approaches.
Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.
✿✿✿
Feature image source: