Skip to content

Instantly share code, notes, and snippets.

@dorneanu
Last active April 17, 2024 00:15
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save dorneanu/cce1cd6711969d581873a88e0257e312 to your computer and use it in GitHub Desktop.
Save dorneanu/cce1cd6711969d581873a88e0257e312 to your computer and use it in GitHub Desktop.
Python: Implement basic plugin architecture with Python and importlib

Implementing a basic plugin architecture shouldn't be a complicated task. The solution described here is working but you still have to import every plugin (inheriting from the base class).

This is my solution:

Basic project structure

$ tree
.
├── main.py
└── plugins
    ├── __init__.py
    ├── plugin_a.py
    ├── plugin_b.py

The base plugin

$ cat plugins/__init__.py

import os
import traceback
from importlib import util


class Base:
    """Basic resource class. Concrete resources will inherit from this one
    """
    plugins = []

    # For every class that inherits from the current,
    # the class name will be added to plugins
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.plugins.append(cls)


# Small utility to automatically load modules
def load_module(path):
    name = os.path.split(path)[-1]
    spec = util.spec_from_file_location(name, path)
    module = util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


# Get current path
path = os.path.abspath(__file__)
dirpath = os.path.dirname(path)

for fname in os.listdir(dirpath):
    # Load only "real modules"
    if not fname.startswith('.') and \
       not fname.startswith('__') and fname.endswith('.py'):
        try:
            load_module(os.path.join(dirpath, fname))
        except Exception:
            traceback.print_exc()

A sample plugin

$ cat plugins/plugin_a.py
import plugins


class PluginA(plugins.Base):

    def __init__(self):
        pass

    def start(self):
        print("Plugin A")

How to use it

$ cat main.py
from plugins import Base

if __name__ == '__main__':
    for p in Base.plugins:
        inst = p()
        inst.start()

Sample run

$ python main.py
Plugin B
Plugin A
@dorneanu
Copy link
Author

I'm glad this helped somehow. However, also make sure you check out/read Eli's post on more fundamental concepts of plugin infrastructures. He has some really interesting thoughts there.

@einball
Copy link

einball commented Oct 9, 2023

Haha, same for me! I've been trying to understand the plugin system for hours on end and find a good method to discover plugins. This is it.

@irfanykywz
Copy link

thank you for code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment