A lightweight Python library for lazy-loading registries with namespace support and type safety.
Project description
lazyregistry
A lightweight Python library for lazy-loading registries with namespace support and type safety
Installation
# Install with pip
$ pip install lazyregistry
# Add to your project with uv
$ uv add "lazyregistry"
Quick Start
from lazyregistry import Registry
registry = Registry(name="plugins")
# Register by import string (lazy - imported on access)
registry["json"] = "json:dumps"
# Register by instance (immediate - already imported)
import pickle
registry["pickle"] = pickle.dumps
# Import happens here
serializer = registry["json"]
Features
- Lazy imports - Defer expensive imports until first access
- Instance registration - Register both import strings and direct objects
- Namespaces - Organize multiple registries
- Type-safe - Full generic type support
- Eager loading - Optional immediate import for critical components
- Pretrained models - Built-in support for save_pretrained/from_pretrained pattern
Examples
Run examples: uv run python examples/<example>.py
1. Plugin System
examples/plugin_system.py - Extensible plugin architecture with decorator-based registration:
from lazyregistry import Registry
PLUGINS = Registry(name="plugins")
def plugin(name: str):
def decorator(cls):
PLUGINS[name] = cls
return cls
return decorator
@plugin("uppercase")
class UppercasePlugin:
def process(self, text: str) -> str:
return text.upper()
# Execute plugins
PluginManager.execute("uppercase", "hello") # "HELLO"
PluginManager.pipeline("hello", "uppercase", "reverse") # "OLLEH"
2. Pretrained Models
examples/pretrained.py - HuggingFace-style save/load with two patterns:
Basic (config only):
from lazyregistry import NAMESPACE
from lazyregistry.pretrained import AutoRegistry, PretrainedConfig, PretrainedMixin
# Each model has its own config with hardcoded model_type
class BertConfig(PretrainedConfig):
model_type: str = "bert"
hidden_size: int = 768
class GPT2Config(PretrainedConfig):
model_type: str = "gpt2"
hidden_size: int = 768
# Base model class
class BaseModel(PretrainedMixin):
config_class = PretrainedConfig
class AutoModel(AutoRegistry):
registry = NAMESPACE["models"]
config_class = PretrainedConfig
type_key = "model_type"
# Register with decorator - models inherit from BaseModel
@AutoModel.register_module("bert")
class BertModel(BaseModel):
config_class = BertConfig
# Or register directly
AutoModel.registry["gpt2"] = "transformers:GPT2Model" # Lazy import
AutoModel.registry["t5"] = T5Model # Direct
# Save and auto-load
model = BertModel(BertConfig(hidden_size=1024))
model.save_pretrained("./model")
loaded = AutoModel.from_pretrained("./model") # Auto-detects type
Advanced (config + custom state):
class Tokenizer(PretrainedMixin):
def __init__(self, config: PretrainedConfig, vocab: dict[str, int] | None = None):
super().__init__(config)
self.vocab = vocab or {}
def save_pretrained(self, path):
super().save_pretrained(path)
# Save additional state (vocabulary)
Path(path).joinpath("vocab.txt").write_text(...)
@classmethod
def from_pretrained(cls, path):
config = cls.config_class.model_validate_json(...)
vocab = ... # Load vocabulary
return cls(config, vocab=vocab)
API Reference
Core Classes
ImportString - String that represents an import path with lazy loading capability
from lazyregistry import ImportString
# Create an import string
import_str = ImportString("json:dumps")
# Load the object when needed
func = import_str.load()
func({"key": "value"}) # '{"key": "value"}'
Registry[K, V] - Named registry with lazy import support
registry = Registry(name="plugins")
# Dict-style assignment (auto-converts strings to ImportString)
registry["key"] = "module:object" # Lazy
registry["key2"] = actual_object # Immediate
# Configure behavior
registry.eager_load = True
registry["key3"] = "module:object" # Eager load
Namespace - Container for multiple registries
from lazyregistry import NAMESPACE
# Direct access to .registry for registration
NAMESPACE["models"]["bert"] = "transformers:BertModel"
NAMESPACE["models"]["gpt2"] = GPT2Model
# Access registered items
model = NAMESPACE["models"]["bert"]
LazyImportDict[K, V] - Base class for custom implementations
Same dict-style API as Registry, but with configurable behavior:
from lazyregistry.registry import LazyImportDict
registry = LazyImportDict()
# Configure behavior via attributes
registry.auto_import_strings = True # Auto-convert strings to ImportString (default: True)
registry.eager_load = False # Load immediately on assignment (default: False)
# Use like a normal dict
registry["key"] = "module:object"
registry.update({"key2": "module:object2"})
Pretrained Pattern
PretrainedMixin - Save/load with Pydantic config
class MyConfig(PretrainedConfig):
model_type: str = "my_model"
class MyModel(PretrainedMixin):
config_class = MyConfig
model.save_pretrained("./path")
loaded = MyModel.from_pretrained("./path")
AutoRegistry - Auto-detect model type from config
Three ways to register:
from lazyregistry.pretrained import PretrainedConfig, PretrainedMixin
# Each model has its own config class
class BertConfig(PretrainedConfig):
model_type: str = "bert"
hidden_size: int = 768
class GPT2Config(PretrainedConfig):
model_type: str = "gpt2"
hidden_size: int = 768
# Base model class
class BaseModel(PretrainedMixin):
config_class = PretrainedConfig
class AutoModel(AutoRegistry):
registry = NAMESPACE["models"]
config_class = PretrainedConfig
type_key = "model_type"
# 1. Decorator registration - models inherit from BaseModel
@AutoModel.register_module("bert")
class BertModel(BaseModel):
config_class = BertConfig
# 2. Direct registration via .registry
AutoModel.registry["gpt2"] = GPT2Model # Direct instance
AutoModel.registry["t5"] = "transformers:T5Model" # Lazy import string
# 3. Bulk registration via .registry.update() - useful for many models
AutoModel.registry.update({
"roberta": RobertaModel,
"albert": "transformers:AlbertModel",
"electra": "transformers:ElectraModel",
})
# Auto-detect and load
loaded = AutoModel.from_pretrained("./path") # Detects type from config
Why?
Before:
# All imports happen upfront
from heavy_module_1 import ClassA
from heavy_module_2 import ClassB
from heavy_module_3 import ClassC
REGISTRY = {"a": ClassA, "b": ClassB, "c": ClassC}
After:
# Import only what you use
from lazyregistry import Registry
registry = Registry(name="components")
registry.register("a", "heavy_module_1:ClassA")
registry.register("b", "heavy_module_2:ClassB")
registry.register("c", "heavy_module_3:ClassC")
# Only ClassA is imported
component = registry["a"]
Testing
Run tests with coverage:
uv run pytest tests/ --cov=lazyregistry --cov-report=term-missing
The test suite includes:
- Core registry tests - LazyImportDict, Registry, Namespace functionality
- Pretrained tests - save/load patterns, AutoRegistry, custom state
- Example tests - Verify all examples run correctly
License
MIT
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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lazyregistry-0.3.0.tar.gz.
File metadata
- Download URL: lazyregistry-0.3.0.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c8d20dc3a59ae31af2e27e5b063dbc76af30b0d62ccb9943c81ba649079f961
|
|
| MD5 |
4406d9a71187d267be237677d87f6bb3
|
|
| BLAKE2b-256 |
da7c31e556482d742f7bc2aed9fea8f66d0a2751df3bd01869524f2086c7f9f2
|
File details
Details for the file lazyregistry-0.3.0-py3-none-any.whl.
File metadata
- Download URL: lazyregistry-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
035d7f1ba93f7632760637d65d29be1a9bb45616cd4b4b32fce5f2cd96f92b1f
|
|
| MD5 |
9a454ddac88b81a23f0d2d9ea8195b75
|
|
| BLAKE2b-256 |
9d3cac27e38e7fcb9573c0a61f24215f5d2a706d729a850b493b2cdccab9a4ad
|