Skip to main content

Simple, flexible, and unopinionated plugin system for Python projects.

Project description

cincodex

Build Status Test Coverage Read The Docs Latest Release Python Version MIT License

Cincodex is a simple, flexible, and unopinionated plugin system for Python projects. Cincodex is designed for both applications and libraries that wish to be extensible at runtime. Plugins can be any Python object including a class, a function, or an instance of an object. The following is a very basic example showing that the application creates a Codex, which is the plugin system, then discovers all available plugins within the ./plugins directory, and the ./plugins/foo/bar.py source file registers a function with the codex.

# app.py
from cincodex import Codex, register_codex

codex = Codex('my_app')
register_codex(codex)

# discover all plugins
codex.discover_plugins('./plugins')

# after this, the `foo.bar` plugin is available and we can call it.
plugin = codex.get('foo.bar')
plugin()
# 'Hello world!'


# ./plugins/foo/bar.py
from app import codex

@codex.register
@codex.metadata(id='foo.bar')
def foo_bar_plugin():
    print('Hello world!')

Cincodex has been tested on Linux and Windows systems but should support other operating systems as well. Cincodex supports Python 3.10 and newer.

Installation

pip install cincodex

Features

  • Cincodex is designed to work with multiple Python packages within a single project. For example, an application can have its own plugin system a multiple dependencies can have their own plugin system and they will not interfere with each other.
  • Cincodex automatically discovers and registers all available plugins within one or more root directories.
  • The method of finding and loading plugins and registering their metadata are all extensible within cincodex. Easily customize the plugin metadata and change how plugins are loaded.
  • The import statement and machinery continues to work properly for discovered plugins, supporting both relative and absolute imports within plugins.
  • A plugin is anything: a class, a function, or an object instance. Cincodex works seamlessly with all plugin types.

A more complete example

The following is a cincodex plugin system where:

  • A plugin base class that all plugin must implement. Each plugin says "hello" in a different language.
  • A custom plugin metadata class
  • Loading plugin from multiple directories

hello.py

This module defines the plugin metadata, plugin base class, and codex.

#
# hello.py
#
from cincodex import Codex, PluginMetadata, register_codex


# First we create a custom plugin metadata class, inheriting from cincodex `PluginMetadata`
class HelloPluginMetadata(PluginMetadata):
    def __init__(self, id: str, *, lang: str):
        super().__init__(id)
        self.lang = lang


# Our plugin system will be class based. So, next we create a base class that all plugins must
# inherit from and implement the `say_hello` method.
class HelloPlugin:
    # __plugin_metadata__ is always set by cincodex with the call to `Codex.register`. We include
    # the field here so that the IDE / type checking knows that __plugin_metadata__ is a class
    # attribute.
    __plugin_metadata__: HelloPluginMetadata

    def say_hello(self) -> None:
        raise NotImplementedError()


# Create and register the codex
codex: Codex[type[HelloPlugin], HelloPluginMetadata] = Codex('hello', HelloPluginMetadata)
register_codex(codex)

builtin_plugins/english.py

Defines a plugin that says "hello" in English.

#
# ./builtin_plugins/english.py
#
from hello import HelloPlugin, codex


@codex.register
@codex.metadata('lang.english', lang='en-us')
class EnglishHello(HelloPlugin):
    def say_hello(self):
        name = input('What is your name? > ')
        print('Hello,', name)

./contrib_plugins/german.py

Defines a plugin that says "hello" in German.

#
# ./contrib_plugins/german.py
#
from hello import HelloPlugin

from cincodex import get_codex

# We registered the codex so we don't technically need to import it
codex = get_codex('hello')


@codex.register
@codex.metadata('lang.german', lang='de')
class GermanLang(HelloPlugin):
    def say_hello(self):
        name = input('Wie heissen sie? > ')
        print('Guten tag,', name)

app.py

Application script that discovers both builtin and contributed plugins and runs each sequentially.

#
# app.py
#
if __name__ == '__main__':
    from hello import HelloPluginMetadata, codex

    # Discover builtin and contributed plugins
    codex.discover_plugins('./builtin_plugins')
    codex.discover_plugins('./contrib_plugins')

    # iterate over all available plugins
    for plugin_cls in codex:
        # `plugin_cls` is a type[HelloPlugin]
        # get the plugin metadata
        metadata = HelloPluginMetadata.get(plugin_cls)
        # our codex registers plugin *classes*, so we need to first instantiate an instance of this
        # plugin
        plugin = plugin_cls()
        print(f'Running plugin: {metadata.id} (lang: {metadata.lang})')
        plugin.say_hello()
        print()

In this example, running python app.py will output the following:

$ python app.py

Running plugin: lang.english (lang: en-us)
What is your name? > Adam
Hello, Adam

Running plugin: lang.german (lang: de)
Wie heissen sie? > Wolfgang
Guten tag, Wolfgang

This example is available in the example_app directory.

API Documentation

Cincodex API documentation is generated by Sphinx and hosted on Read the Docs. The API documentation includes more detailed and complex examples and information on how to extend cincodex.

License

Cincodex is licensed under the MIT permissive license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cincodex-0.1.0.tar.gz (17.2 kB view hashes)

Uploaded Source

Built Distribution

cincodex-0.1.0-py3-none-any.whl (10.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page