Examples

This page contains practical examples of using metaclass-registry.

Basic Plugin System

Create a simple plugin system with auto-registration:

from metaclass_registry import AutoRegisterMeta

class PluginBase(metaclass=AutoRegisterMeta):
    __registry_key__ = 'plugin_name'
    plugin_name = None

    def execute(self):
        raise NotImplementedError

class DataProcessor(PluginBase):
    plugin_name = 'processor'

    def execute(self):
        return "Processing data..."

class DataValidator(PluginBase):
    plugin_name = 'validator'

    def execute(self):
        return "Validating data..."

# Use the registry
PLUGINS = PluginBase.__registry__
for name, plugin_class in PLUGINS.items():
    plugin = plugin_class()
    print(f"{name}: {plugin.execute()}")

Microscope Handler System

Real-world example of a microscope handler registry with secondary registries:

from metaclass_registry import (
    AutoRegisterMeta,
    SecondaryRegistry,
    PRIMARY_KEY,
    make_suffix_extractor,
)

# Secondary registry for metadata handlers
METADATA_HANDLERS = {}

class MicroscopeHandler(metaclass=AutoRegisterMeta):
    __registry_key__ = 'microscope_type'
    __key_extractor__ = make_suffix_extractor('Handler')
    __secondary_registries__ = [
        SecondaryRegistry(
            registry_dict=METADATA_HANDLERS,
            key_source=PRIMARY_KEY,
            attr_name='metadata_handler_class'
        )
    ]
    microscope_type = None
    metadata_handler_class = None

    def read_image(self, path):
        raise NotImplementedError

class ImageXpressMetadata:
    def parse(self, path):
        return {"vendor": "Molecular Devices"}

class ImageXpressHandler(MicroscopeHandler):
    metadata_handler_class = ImageXpressMetadata

    def read_image(self, path):
        return f"Reading ImageXpress image from {path}"

class OperettaMetadata:
    def parse(self, path):
        return {"vendor": "PerkinElmer"}

class OperettaHandler(MicroscopeHandler):
    metadata_handler_class = OperettaMetadata

    def read_image(self, path):
        return f"Reading Operetta image from {path}"

# Use the registries
HANDLERS = MicroscopeHandler.__registry__
print(list(HANDLERS.keys()))  # ['imagexpress', 'operetta']
print(list(METADATA_HANDLERS.keys()))  # ['imagexpress', 'operetta']

# Get a handler
handler = HANDLERS['imagexpress']()
print(handler.read_image('/path/to/image'))

Storage Backend System

Example of a storage backend registry with inheritance:

from metaclass_registry import AutoRegisterMeta

class StorageBackend(metaclass=AutoRegisterMeta):
    __registry_key__ = 'backend_type'
    __skip_if_no_key__ = True
    backend_type = None

    def read(self, key):
        raise NotImplementedError

    def write(self, key, data):
        raise NotImplementedError

class DiskStorage(StorageBackend):
    backend_type = 'disk'

    def read(self, key):
        return f"Reading {key} from disk"

    def write(self, key, data):
        return f"Writing {key} to disk"

class MemoryStorage(StorageBackend):
    backend_type = 'memory'

    def __init__(self):
        self.store = {}

    def read(self, key):
        return self.store.get(key)

    def write(self, key, data):
        self.store[key] = data

class ZarrStorage(StorageBackend):
    backend_type = 'zarr'

    def read(self, key):
        return f"Reading {key} from Zarr"

    def write(self, key, data):
        return f"Writing {key} to Zarr"

# Factory function
def get_storage(backend_type='disk'):
    BACKENDS = StorageBackend.__registry__
    backend_class = BACKENDS.get(backend_type)
    if not backend_class:
        raise ValueError(f"Unknown backend: {backend_type}")
    return backend_class()

# Use the factory
storage = get_storage('memory')
storage.write('key1', 'value1')
print(storage.read('key1'))

Custom Key Extractor

Example of using custom key extraction logic:

from metaclass_registry import AutoRegisterMeta

def extract_version(class_name, cls):
    """Extract version from class name like 'V1Handler' -> 'v1'."""
    import re
    match = re.match(r'V(\d+)', class_name)
    if match:
        return f'v{match.group(1)}'
    return None

class APIHandler(metaclass=AutoRegisterMeta):
    __registry_key__ = 'version'
    __key_extractor__ = extract_version
    version = None

    def handle_request(self, request):
        raise NotImplementedError

class V1Handler(APIHandler):
    def handle_request(self, request):
        return f"V1: {request}"

class V2Handler(APIHandler):
    def handle_request(self, request):
        return f"V2: {request}"

class V3Handler(APIHandler):
    def handle_request(self, request):
        return f"V3: {request}"

# Route based on version
HANDLERS = APIHandler.__registry__
print(list(HANDLERS.keys()))  # ['v1', 'v2', 'v3']

def route_request(version, request):
    handler_class = HANDLERS.get(version)
    if not handler_class:
        raise ValueError(f"Unsupported API version: {version}")
    handler = handler_class()
    return handler.handle_request(request)

print(route_request('v2', 'GET /users'))  # V2: GET /users

Multi-Level Inheritance

Example showing registry inheritance across multiple levels:

from metaclass_registry import AutoRegisterMeta

class BaseProcessor(metaclass=AutoRegisterMeta):
    __registry_key__ = 'name'
    name = None

class ImageProcessor(BaseProcessor):
    """All image processors share the same registry."""
    pass

class VideoProcessor(BaseProcessor):
    """All video processors share the same registry."""
    pass

class JPEGProcessor(ImageProcessor):
    name = 'jpeg'

    def process(self, data):
        return "Processing JPEG"

class PNGProcessor(ImageProcessor):
    name = 'png'

    def process(self, data):
        return "Processing PNG"

class MP4Processor(VideoProcessor):
    name = 'mp4'

    def process(self, data):
        return "Processing MP4"

# All share the same registry
assert ImageProcessor.__registry__ is BaseProcessor.__registry__
assert VideoProcessor.__registry__ is BaseProcessor.__registry__

# All processors in one place
PROCESSORS = BaseProcessor.__registry__
print(list(PROCESSORS.keys()))  # ['jpeg', 'png', 'mp4']