While updating my PowerPlug ( pypi ) project, I implemented a pattern I have started using in a few other places that I use entry-points .
By creating a custom enum using one of Django’s enumeration-types , you can make it much easier to load these modules.
from importlib import metadata
from django.db import models
from django.utils.translation import gettext_lazy as _
class EntryPoints(models.TextChoices):
APPS = "powerplug.apps", _("Installed Apps")
REST = "powerplug.rest", _("Installed APIs")
SIGNAL = "powerplug.signal", _("Installed Signals")
TASK = "powerplug.task", _("Installed Tasks")
URLS = "powerplug.urls", _("Installed URLs")
CONTEXT = "powerplug.context", _("Installed context processor")
def group(self) -> metadata.EntryPoints:
return metadata.entry_points(group=self.value)
Using an enum like this, makes it very easy to load one of our entry points, while keeping all the definitions for our app in a single place.
from powerplug.config import EntryPoints
for entry in EntryPoints.TASK.group():
try:
# Attempt to load our entry point
klass = entry.load()
except ImportError:
print("Unable to load", entry)
else:
# Create instance of our Class
obj = klass()
Being able to use an Enum makes this more convenient, and we can use our browser’s code completion and type checking, instead of trying to ensure we get the entry point string correct everywhere.
We could have used a regular enum , though using Django’s enum type gives us an extra, localized label that we can use while debugging.
from powerplug import config
for entry_point in config.EntryPoints:
print(entry_point.label)
for entry in entry_point.group():
try:
entry.load()
except ImportError:
print("\tUnable to load", entry)
else:
print("\tLoaded", entry)