Skip to main content

Convoke

A decentralized async-first app configuration toolkit that tries to do things right.

from convoke.configs import BaseConfig, env_field
from convoke.bases import Base

class ComponentConfig(BaseConfig):
# Get your stuff done
project.do_stuff()

Features

  • Type-safe, environment-based configuration
  • Decentralized app configurations

Installation

With Pip:

pip install convoke

Configuration

Configurations are modular, inspired by Python's dataclasses module:

>>> from convoke.configs import BaseConfig, env_field

>>> class CacheConfig(BaseConfig):
...     """Configuration for the caching framework."""
...
...     CACHE_STORAGE_URI: str = env_field(
...         default="memory://",
...         doc="""A configuration URI pointing to the desired cache backend.
...
...         For instance, for a simple Redis-based cache, use:
...
...             redis://localhost:6379/mycachepath
...         """,
...     )
...

In this example, instantiating CacheConfig() will read CACHE_STORAGE_URI from the process environment (os.environ), defaulting to 'memory://' if nothing is defined.

Rather than defining one singular configuration (e.g. Django's settings.py), convoke encourages defining modular, limited-scope configurations close to where the settings will be used.

Configuration discovery & .env files

In order to discover what configuration values an application needs, convoke provides a .env file generator:

>>> from convoke.configs import generate_dot_env

>>> print(generate_dot_env(BaseConfig.gather_settings()))
################################
## convoke.configs.BaseConfig ##
################################
##
## Base settings common to all configurations

# ------------------
# -- DEBUG (bool) --
# ------------------
#
# Development mode?

DEBUG=""

# --------------------
# -- TESTING (bool) --
# --------------------
#
# Testing mode?

TESTING=""


##########################
## __main__.CacheConfig ##
##########################
##
## Configuration for the caching framework.

# -----------------------------
# -- CACHE_STORAGE_URI (str) --
# -----------------------------
#
# A configuration URI pointing to the desired cache backend.
#
# For instance, for a simple Redis-based cache, use:
#
#     redis://localhost:6379/mycachepath

CACHE_STORAGE_URI="memory://"

Modular apps & signals

Inspired by Django's AppConfig, convoke provides an abstraction called "bases":

In foo.py:

from convoke.bases import Base
from convoke.configs import BaseConfig, env_field
from convoke.signals import Signal


class FOO(Signal):
    pass


class FooConfig(BaseConfig):
    BAR: str = env_field(default="baz")


class Main(Base):
    config_class = FooConfig

    foos: list[FOO.Message] = Base.field(init=False)

    # Circular dependency!
    dependencies = ["baz"]

    def on_init(self):
        self.foos = []

    @Base.responds(FOO)
    def on_foo(self, obj: FOO.Message):  # pragma: nocover
        self.foos.append(obj)

In bar.py:

from convoke.bases import Base
from convoke.signals import Signal


class BAR(Signal):
    pass


class Main(Base):
    dependencies = ["baz"]

    bars: list[BAR.Message] = Base.field(default_factory=list)

    @Base.responds(BAR)
    async def on_bar(self, obj):  # pragma: nocover
        self.bars.append(obj)

In baz.py:

from typing import Callable

from convoke.bases import Base
from convoke.mountpoints import Mountpoint
from convoke.signals import Signal


class BAZ(Signal):
    pass


class ThingyMadoodle(Mountpoint):
    pass


class Main(Base):
    # Circular dependency!
    dependencies = ["foo"]

    things: list[str] = Base.field(default_factory=list)
    other_things: list[str] = Base.field(default_factory=list)

    @Base.responds(BAZ)
    def on_baz(self, obj: BAZ.Message):
        for thinger in self.thingers:
            thinger(obj.value)

    @property
    def thingers(self) -> list[Callable[[str], None]]:
        return list(self.hq.mountpoints[ThingyMadoodle].mounted)

    @ThingyMadoodle.register
    def do_a_thing(self, value):
        self.things.append(value)

    @ThingyMadoodle.register
    def do_another_thing(self, value):
        self.other_things.append(value)

Each Base provides a platform for building a modular app upon, with interdependencies between Bases. Dependencies are allowed to be circular, because bases are instantiated separately from import time by the "main base" known as the HQ:

>>> hq = HQ(config=config)

>>> hq.load_dependencies(dependencies=["foo", "bar"])

The HQ manages loading and instantiating bases, connecting signal handlers, etc. The main application can maintain a reference to the HQ. The HQ class also maintains an HQ.current_hq context variable which contains a reference to the HQ instance for the current thread, if one exists.

Signals

Convoke signals are presently async-only.

Unlike most signals frameworks (e.g. blinker or Django), signals are not global. Instead, signals are passed within the network of bases established by the HQ:

>>> from foo import FOO

>>> await FOO.send(value='blah', using=hq)

>>> await FOO.send(value='blah')  # <-- uses HQ.current_hq

Contribute

License

The project is licensed under the BSD 3-clause license.

Metadata

Release files for convoke 2.2.9

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

Source distribution (sdist)

Source distribution for convoke 2.2.9
File Size Uploaded Provenance
convoke-2.2.9.tar.gz 17.7 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 35.3 kB

Release files / convoke-2.2.9.tar.gz

Download URL convoke-2.2.9.tar.gz
Size 17.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c7b25a8bbaab5d1b4a365517ec71056ed771ea1c234de5e4b9162eb1211780eb
BLAKE2b-256 checksum
How to use checksums
8ac7610f995fce0c24f1916083b24fc3cc20f4978066b856101accdaa56f174c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.7.1 CPython/3.11.7 Linux/6.2.0-1019-azure

Release files / convoke-2.2.9-py3-none-any.whl

Download URL convoke-2.2.9-py3-none-any.whl
Size 17.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec9b0ea52c5cbde133537338436355f16cd237de0a5e1ab1f38e65d0a4034260
BLAKE2b-256 checksum
How to use checksums
5c754c0df1dac5a8fb2e10cafd1d804017ce7f73a716153d892986d5e8aac49c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.7.1 CPython/3.11.7 Linux/6.2.0-1019-azure

Security for convoke 2.2.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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