Simple, flexible, and unopinionated plugin system for Python projects.
Project description
cincodex
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | c5ef87d6c2b672add3bbd9f61490ef7afc1deb4a80eafe1184ba574b73063ee0 |
|
MD5 | a72ce06e95ea8de8ceb4c170c3a18b6d |
|
BLAKE2b-256 | db0e5a8df05585f5e036720ce0e727e795fe8a47c83748bf991ac590c9c909e5 |
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
Algorithm | Hash digest | |
---|---|---|
SHA256 | 308bc1673b19e970dda31727e17eae282cf792a3dbfdad1bd79fb6e36e60dbae |
|
MD5 | c9395659186800ae023cee9eb43b874a |
|
BLAKE2b-256 | 020d9c900dcd9ff2c552b70fc6f77ca03ef522fc001204182d9f76322157dcfd |