Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

metaclass-registry

Zero-boilerplate metaclass-driven plugin registry system with lazy discovery and caching

PyPI version Documentation Status Python 3.10+ License: MIT Coverage

Features

  • Zero Boilerplate: No custom metaclasses, no manual registry creation, just class attributes
  • Lazy Discovery: Plugins discovered automatically on first access
  • Registry Inheritance: Child classes inherit parent's registry for clean interface hierarchies
  • Secondary Registries: Auto-populate related registries from primary registry
  • Persistent Caching: Cache discovery results across process restarts
  • Explicit Discovery: Package scans occur only when the nominal root declares them
  • Nominal Registry Families: Stable key-axis declarations with RegistryFamily and RegistryKeyAttribute
  • Registered Enums: RegisteredEnumMeta for enum members backed by a nominal registry
  • Type-Safe: Full type hints and mypy support

Quick Start

from metaclass_registry import AutoRegisterMeta, RegistryFamily

# Define the nominal family and its semantic key axis.
class PluginBase(metaclass=AutoRegisterMeta):
    __registry_family__ = RegistryFamily(
        "plugin_name",
        registry_name="plugin",
    )
    __registry__ = {}  # Local script: no package discovery required.
    plugin_name = None

# Access the auto-created registry
PLUGINS = PluginBase.__registry__

# Define plugins - they auto-register!
class MyPlugin(PluginBase):
    plugin_name = 'my_plugin'
    
    def run(self):
        return "Hello from my plugin!"

# Use the registry
print(list(PLUGINS.keys()))  # ['my_plugin']
plugin = PLUGINS['my_plugin']()
print(plugin.run())  # "Hello from my plugin!"

RegistryFamily is the preferred declaration for an ordinary nominal root. It installs the compatibility attributes used by AutoRegisterMeta while keeping the key axis in one object. Consumers should iterate or query PluginBase.__registry__ rather than maintain a parallel plugin mapping. The explicit plain registry is appropriate for this one-file example. Ordinary roots register declarations as their modules are imported; package discovery is opt-in through RegistryConfig.discovery_package.

Use RegistryConfig when a family needs a supplied registry, a key extractor, lazy discovery, secondary registries, or custom logging:

from metaclass_registry import (
    AutoRegisterMeta,
    RegistryConfig,
    make_suffix_extractor,
)

HANDLERS = {}
HANDLER_CONFIG = RegistryConfig(
    registry_dict=HANDLERS,
    key_attribute="handler_type",
    key_extractor=make_suffix_extractor("Handler"),
    skip_if_no_key=True,
    registry_name="handler",
)

class Handler(metaclass=AutoRegisterMeta):
    __registry_config__ = HANDLER_CONFIG
    handler_type = None

class FileHandler(Handler):
    pass

assert HANDLERS["file"] is FileHandler

Package discovery

Package discovery is explicit in 0.2.0. A nominal root that must discover declarations which are not otherwise imported owns that behavior directly:

from metaclass_registry import AutoRegisterMeta, LazyDiscoveryDict, RegistryConfig

class Plugin(metaclass=AutoRegisterMeta):
    __registry_config__ = RegistryConfig(
        registry_dict=LazyDiscoveryDict(),
        key_attribute="plugin_name",
        discovery_package="my_application.plugins",
        discovery_recursive=False,
    )
    plugin_name = None

Use discovery_recursive=True only when plugin declarations live in subpackages. Without discovery_package, the registry contains declarations imported by the application and never scans the surrounding package implicitly.

Installation

pip install metaclass-registry

Why metaclass-registry?

Most plugin systems require boilerplate code:

Before (Traditional approach):

# Custom metaclass per registry
class PluginMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if hasattr(cls, 'plugin_name') and cls.plugin_name:
            PLUGINS[cls.plugin_name] = cls
        return cls

# Manual registry creation
PLUGINS = {}

# Base class with custom metaclass
class PluginBase(metaclass=PluginMeta):
    plugin_name = None

After (metaclass-registry):

# One nominal family declaration owns the registry protocol.
class PluginBase(metaclass=AutoRegisterMeta):
    __registry_family__ = RegistryFamily("plugin_name")
    plugin_name = None

# Access auto-created registry
PLUGINS = PluginBase.__registry__

Advanced Features

Registry Inheritance

class BackendBase(metaclass=AutoRegisterMeta):
    __registry_family__ = RegistryFamily("backend_type")
    backend_type = None

class StorageBackend(BackendBase):
    pass  # Inherits BackendBase.__registry__

class ReadOnlyBackend(BackendBase):
    pass  # Also inherits BackendBase.__registry__

# All share the SAME registry!
assert StorageBackend.__registry__ is BackendBase.__registry__

Secondary Registries

Pass SecondaryRegistry declarations through the family's authoritative RegistryConfig. Secondary mappings are derived indexes; populate them only through registration, never through a second manual registration path.

Custom Key Extractors

Set RegistryConfig.key_extractor when names must be derived. The extractor receives (class_name, cls) and should return the semantic key. Prefer an explicit family key attribute when the declaration can own the value directly.

Documentation

Full documentation available at metaclass-registry.readthedocs.io

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome through the repository and its issue tracker.

Credits

Developed by Tristan Simas as part of the OpenHCS project.

Download files

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

Source Distribution

metaclass_registry-0.2.0rc1.tar.gz (28.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

metaclass_registry-0.2.0rc1-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file metaclass_registry-0.2.0rc1.tar.gz.

File metadata

  • Download URL: metaclass_registry-0.2.0rc1.tar.gz
  • Upload date:
  • Size: 28.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for metaclass_registry-0.2.0rc1.tar.gz
Algorithm Hash digest
SHA256 63ed66ac7bf0d98ae8a4e6317a180e6f106c909daf2e511a9d293f5a2f1f8a08
MD5 7ab0c87aac36f68f807a4a46127b263b
BLAKE2b-256 274719a38e9a1690c225ec53939b9904ad93fffc948ba02b621c88a9692677d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for metaclass_registry-0.2.0rc1.tar.gz:

Publisher: publish.yml on OpenHCSDev/metaclass-registry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file metaclass_registry-0.2.0rc1-py3-none-any.whl.

File metadata

File hashes

Hashes for metaclass_registry-0.2.0rc1-py3-none-any.whl
Algorithm Hash digest
SHA256 019c111e1220170821252c7325bdd5fa32d1105db145f8fc7be4d6a2f7bc3a34
MD5 61064eb086c4a2df5a2de2ab2ecea909
BLAKE2b-256 d66377e1726832d580880fb3bdd80b7e046ccc38dc4c1a677622265885d62837

See more details on using hashes here.

Provenance

The following attestation bundles were made for metaclass_registry-0.2.0rc1-py3-none-any.whl:

Publisher: publish.yml on OpenHCSDev/metaclass-registry

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page