Skip to main content

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.

Release files for cincodex 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cincodex 0.1.0
File Size Uploaded
cincodex-0.1.0.tar.gz 17.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cincodex 0.1.0
File Interpreter ABI Platform
cincodex-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.7 kB

Release files / cincodex-0.1.0.tar.gz

Download URL cincodex-0.1.0.tar.gz
Size 17.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c5ef87d6c2b672add3bbd9f61490ef7afc1deb4a80eafe1184ba574b73063ee0
BLAKE2b-256 checksum
How to use checksums
db0e5a8df05585f5e036720ce0e727e795fe8a47c83748bf991ac590c9c909e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.3.2 CPython/3.10.10 Windows/10

Release files / cincodex-0.1.0-py3-none-any.whl

Download URL cincodex-0.1.0-py3-none-any.whl
Size 10.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
308bc1673b19e970dda31727e17eae282cf792a3dbfdad1bd79fb6e36e60dbae
BLAKE2b-256 checksum
How to use checksums
020d9c900dcd9ff2c552b70fc6f77ca03ef522fc001204182d9f76322157dcfd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.3.2 CPython/3.10.10 Windows/10

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page