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.

116-feature-image.png
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.

❶ The timmins application:

▶️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.

❶ The timmins application:

⓵ 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: