Backend Registry
The backend registry system provides automatic discovery and registration of storage backends using metaclass-based registration.
BackendRegistry
The BackendRegistry class manages available storage backends and provides access to backend instances.
Example:
from polystore import BackendRegistry
# Create registry (auto-discovers backends)
registry = BackendRegistry()
# List available backends
print(list(registry.keys()))
# Output: ['memory', 'disk', 'zarr']
# Access a backend
disk_backend = registry['disk']
# Use with FileManager
from polystore import FileManager
fm = FileManager(registry)
Auto-Registration
Backends are automatically registered when their classes are defined, using the AutoRegisterMeta metaclass.
How It Works
Backends inherit from
StorageBackendorReadOnlyBackendThey define a
_backend_typeclass attributeThe metaclass automatically registers them in the global registry
BackendRegistrydiscovers and instantiates them on first access
Example:
from polystore import StorageBackend
class MyBackend(StorageBackend):
_backend_type = 'my_backend' # Registration key
def save(self, data, file_path, **kwargs):
# Implementation
pass
def load(self, file_path, **kwargs):
# Implementation
pass
# ... other required methods
Once defined, the backend is automatically available:
from polystore import BackendRegistry
registry = BackendRegistry()
assert 'my_backend' in registry
AutoRegisterMeta
The registration system uses AutoRegisterMeta from the external metaclass-registry package.
- Key Features:
Automatic class registration at definition time
Support for abstract base classes (not registered)
Clean separation of concerns
No manual registration required
- Registry Key:
Backends use
_backend_typeas the registry key:
class DiskBackend(StorageBackend):
_backend_type = 'disk' # Registered as 'disk'
- Abstract Classes:
Base classes with abstract methods are not registered:
from abc import abstractmethod
class BaseBackend(StorageBackend):
_backend_type = 'base' # Not registered (abstract)
@abstractmethod
def custom_method(self):
pass
Backend Discovery
The registry discovers backends through import-time registration:
Module Import: When a module containing a backend class is imported, the class is defined
Metaclass Hook:
AutoRegisterMeta.__new__()is called during class creationRegistration: The backend is added to the global registry
Discovery:
BackendRegistryaccesses the registry to find all registered backends
Import Trigger:
Backends must be imported before they can be discovered. The core backends
(disk, memory, zarr) are imported automatically by BackendRegistry.
For custom backends, ensure they are imported:
# Import your custom backend module
import my_package.my_backend
# Now it's available in the registry
from polystore import BackendRegistry
registry = BackendRegistry()
assert 'my_backend' in registry
Lazy Instantiation
The registry uses lazy instantiation for efficiency:
Discovery: All backend classes are discovered at registry creation
Instantiation: Backends are instantiated only when first accessed
Caching: Instances are cached for reuse
Example:
from polystore import BackendRegistry
# Registry created, backends discovered but not instantiated
registry = BackendRegistry()
# First access instantiates the backend
disk = registry['disk'] # DiskBackend() called here
# Subsequent access returns cached instance
disk2 = registry['disk'] # Returns same instance
assert disk is disk2
Manual Registration
While auto-registration is preferred, you can manually register backends:
from polystore import BackendRegistry, MemoryBackend
# Create custom registry
registry = {}
# Manually register backends
registry['memory'] = MemoryBackend()
registry['custom'] = MyCustomBackend()
# Use with FileManager
from polystore import FileManager
fm = FileManager(registry)
Thread Safety
Backend instances are shared across the registry by default. If your backends maintain mutable state, consider thread-safety implications:
Shared State:
# Same backend instance used by all FileManagers
registry = BackendRegistry()
fm1 = FileManager(registry)
fm2 = FileManager(registry)
# fm1 and fm2 share the same backend instances
Isolated State:
# Create separate backend instances per FileManager
from polystore import MemoryBackend, DiskBackend
registry1 = {'memory': MemoryBackend(), 'disk': DiskBackend()}
registry2 = {'memory': MemoryBackend(), 'disk': DiskBackend()}
fm1 = FileManager(registry1)
fm2 = FileManager(registry2)
Best Practices
Use Auto-Registration: Prefer metaclass registration over manual registration
Import Early: Import backend modules early to ensure discovery
Single Registry: Use a single
BackendRegistryinstance per applicationThread Safety: Be aware of shared backend instances in multi-threaded applications
Naming: Use lowercase, descriptive names for
_backend_type
See Also
Storage Backends - Available storage backends
Creating Custom Backends - Creating custom backends
metaclass-registry - External registration package