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 details)

Uploaded Source

Built Distribution

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

Uploaded Python 3

File details

Details for the file cincodex-0.1.0.tar.gz.

File metadata

  • Download URL: cincodex-0.1.0.tar.gz
  • Upload date:
  • Size: 17.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.3.2 CPython/3.10.10 Windows/10

File hashes

Hashes for cincodex-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c5ef87d6c2b672add3bbd9f61490ef7afc1deb4a80eafe1184ba574b73063ee0
MD5 a72ce06e95ea8de8ceb4c170c3a18b6d
BLAKE2b-256 db0e5a8df05585f5e036720ce0e727e795fe8a47c83748bf991ac590c9c909e5

See more details on using hashes here.

File details

Details for the file cincodex-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cincodex-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.3.2 CPython/3.10.10 Windows/10

File hashes

Hashes for cincodex-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 308bc1673b19e970dda31727e17eae282cf792a3dbfdad1bd79fb6e36e60dbae
MD5 c9395659186800ae023cee9eb43b874a
BLAKE2b-256 020d9c900dcd9ff2c552b70fc6f77ca03ef522fc001204182d9f76322157dcfd

See more details on using hashes here.

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