# lazyregistry
[](https://github.com/milkclouds/lazyregistry/actions/workflows/ci.yml)
[](https://codecov.io/gh/milkclouds/lazyregistry)
[](https://pypi.python.org/pypi/lazyregistry)
[](https://pypi.org/project/lazyregistry/)
[](https://opensource.org/licenses/MIT)
[](https://github.com/astral-sh/ruff)
A lightweight Python library for lazy-loading registries with namespace support and type safety
## Installation
```bash
# Install with pip
$ pip install lazyregistry
# Add to your project with uv
$ uv add "lazyregistry"
```
## Quick Start
```python
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`](examples/plugin_system.py) - Extensible plugin architecture with decorator-based registration:
```python
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`](examples/pretrained.py) - HuggingFace-style save/load with two patterns:
**Basic (config only):**
```python
from lazyregistry import NAMESPACE
from lazyregistry.pretrained import AutoRegistry, PretrainedConfig, PretrainedMixin
# Each model has its own config with type identifier
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):**
```python
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
```python
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
```python
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
```python
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:
```python
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
```python
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
The `type_key` parameter (defaults to "model_type") determines which config field is used for type detection.
Three ways to register:
```python
from lazyregistry.pretrained import PretrainedConfig, PretrainedMixin
# Each model has its own config class with type identifier
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" # Can use any field name
# 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:**
```python
# 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:**
```python
# 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:
```bash
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
Raw data
{
"_id": null,
"home_page": null,
"name": "lazyregistry",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "lazy-loading, namespace, plugin, pretrained, registry, type-safe",
"author": null,
"author_email": "Suhwan Choi <milkclouds00@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/d1/3b/48a1f0f4d4b2643c93de7885a69b00f9982efce7994fc0e9f6c857e71774/lazyregistry-0.3.2.tar.gz",
"platform": null,
"description": "# lazyregistry\n\n[](https://github.com/milkclouds/lazyregistry/actions/workflows/ci.yml)\n[](https://codecov.io/gh/milkclouds/lazyregistry)\n[](https://pypi.python.org/pypi/lazyregistry)\n[](https://pypi.org/project/lazyregistry/)\n[](https://opensource.org/licenses/MIT)\n[](https://github.com/astral-sh/ruff)\n\nA lightweight Python library for lazy-loading registries with namespace support and type safety\n\n## Installation\n\n```bash\n# Install with pip\n$ pip install lazyregistry\n\n# Add to your project with uv\n$ uv add \"lazyregistry\"\n```\n\n## Quick Start\n\n```python\nfrom lazyregistry import Registry\n\nregistry = Registry(name=\"plugins\")\n\n# Register by import string (lazy - imported on access)\nregistry[\"json\"] = \"json:dumps\"\n\n# Register by instance (immediate - already imported)\nimport pickle\nregistry[\"pickle\"] = pickle.dumps\n\n# Import happens here\nserializer = registry[\"json\"]\n```\n\n## Features\n\n- **Lazy imports** - Defer expensive imports until first access\n- **Instance registration** - Register both import strings and direct objects\n- **Namespaces** - Organize multiple registries\n- **Type-safe** - Full generic type support\n- **Eager loading** - Optional immediate import for critical components\n- **Pretrained models** - Built-in support for save_pretrained/from_pretrained pattern\n\n## Examples\n\nRun examples: `uv run python examples/<example>.py`\n\n### 1. Plugin System\n\n[`examples/plugin_system.py`](examples/plugin_system.py) - Extensible plugin architecture with decorator-based registration:\n\n```python\nfrom lazyregistry import Registry\n\nPLUGINS = Registry(name=\"plugins\")\n\ndef plugin(name: str):\n def decorator(cls):\n PLUGINS[name] = cls\n return cls\n return decorator\n\n@plugin(\"uppercase\")\nclass UppercasePlugin:\n def process(self, text: str) -> str:\n return text.upper()\n\n# Execute plugins\nPluginManager.execute(\"uppercase\", \"hello\") # \"HELLO\"\nPluginManager.pipeline(\"hello\", \"uppercase\", \"reverse\") # \"OLLEH\"\n```\n\n### 2. Pretrained Models\n\n[`examples/pretrained.py`](examples/pretrained.py) - HuggingFace-style save/load with two patterns:\n\n**Basic (config only):**\n```python\nfrom lazyregistry import NAMESPACE\nfrom lazyregistry.pretrained import AutoRegistry, PretrainedConfig, PretrainedMixin\n\n# Each model has its own config with type identifier\nclass BertConfig(PretrainedConfig):\n model_type: str = \"bert\"\n hidden_size: int = 768\n\nclass GPT2Config(PretrainedConfig):\n model_type: str = \"gpt2\"\n hidden_size: int = 768\n\n# Base model class\nclass BaseModel(PretrainedMixin):\n config_class = PretrainedConfig\n\nclass AutoModel(AutoRegistry):\n registry = NAMESPACE[\"models\"]\n config_class = PretrainedConfig\n type_key = \"model_type\"\n\n# Register with decorator - models inherit from BaseModel\n@AutoModel.register_module(\"bert\")\nclass BertModel(BaseModel):\n config_class = BertConfig\n\n# Or register directly\nAutoModel.registry[\"gpt2\"] = \"transformers:GPT2Model\" # Lazy import\nAutoModel.registry[\"t5\"] = T5Model # Direct\n\n# Save and auto-load\nmodel = BertModel(BertConfig(hidden_size=1024))\nmodel.save_pretrained(\"./model\")\nloaded = AutoModel.from_pretrained(\"./model\") # Auto-detects type\n```\n\n**Advanced (config + custom state):**\n```python\nclass Tokenizer(PretrainedMixin):\n def __init__(self, config: PretrainedConfig, vocab: dict[str, int] | None = None):\n super().__init__(config)\n self.vocab = vocab or {}\n\n def save_pretrained(self, path):\n super().save_pretrained(path)\n # Save additional state (vocabulary)\n Path(path).joinpath(\"vocab.txt\").write_text(...)\n\n @classmethod\n def from_pretrained(cls, path):\n config = cls.config_class.model_validate_json(...)\n vocab = ... # Load vocabulary\n return cls(config, vocab=vocab)\n```\n\n## API Reference\n\n### Core Classes\n\n**`ImportString`** - String that represents an import path with lazy loading capability\n```python\nfrom lazyregistry import ImportString\n\n# Create an import string\nimport_str = ImportString(\"json:dumps\")\n\n# Load the object when needed\nfunc = import_str.load()\nfunc({\"key\": \"value\"}) # '{\"key\": \"value\"}'\n```\n\n**`Registry[K, V]`** - Named registry with lazy import support\n```python\nregistry = Registry(name=\"plugins\")\n\n# Dict-style assignment (auto-converts strings to ImportString)\nregistry[\"key\"] = \"module:object\" # Lazy\nregistry[\"key2\"] = actual_object # Immediate\n\n# Configure behavior\nregistry.eager_load = True\nregistry[\"key3\"] = \"module:object\" # Eager load\n```\n\n**`Namespace`** - Container for multiple registries\n```python\nfrom lazyregistry import NAMESPACE\n\n# Direct access to .registry for registration\nNAMESPACE[\"models\"][\"bert\"] = \"transformers:BertModel\"\nNAMESPACE[\"models\"][\"gpt2\"] = GPT2Model\n\n# Access registered items\nmodel = NAMESPACE[\"models\"][\"bert\"]\n```\n\n**`LazyImportDict[K, V]`** - Base class for custom implementations\n\nSame dict-style API as `Registry`, but with configurable behavior:\n```python\nfrom lazyregistry.registry import LazyImportDict\n\nregistry = LazyImportDict()\n\n# Configure behavior via attributes\nregistry.auto_import_strings = True # Auto-convert strings to ImportString (default: True)\nregistry.eager_load = False # Load immediately on assignment (default: False)\n\n# Use like a normal dict\nregistry[\"key\"] = \"module:object\"\nregistry.update({\"key2\": \"module:object2\"})\n```\n\n### Pretrained Pattern\n\n**`PretrainedMixin`** - Save/load with Pydantic config\n```python\nclass MyConfig(PretrainedConfig):\n model_type: str = \"my_model\"\n\nclass MyModel(PretrainedMixin):\n config_class = MyConfig\n\nmodel.save_pretrained(\"./path\")\nloaded = MyModel.from_pretrained(\"./path\")\n```\n\n**`AutoRegistry`** - Auto-detect model type from config\n\nThe `type_key` parameter (defaults to \"model_type\") determines which config field is used for type detection.\n\nThree ways to register:\n```python\nfrom lazyregistry.pretrained import PretrainedConfig, PretrainedMixin\n\n# Each model has its own config class with type identifier\nclass BertConfig(PretrainedConfig):\n model_type: str = \"bert\"\n hidden_size: int = 768\n\nclass GPT2Config(PretrainedConfig):\n model_type: str = \"gpt2\"\n hidden_size: int = 768\n\n# Base model class\nclass BaseModel(PretrainedMixin):\n config_class = PretrainedConfig\n\nclass AutoModel(AutoRegistry):\n registry = NAMESPACE[\"models\"]\n config_class = PretrainedConfig\n type_key = \"model_type\" # Can use any field name\n\n# 1. Decorator registration - models inherit from BaseModel\n@AutoModel.register_module(\"bert\")\nclass BertModel(BaseModel):\n config_class = BertConfig\n\n# 2. Direct registration via .registry\nAutoModel.registry[\"gpt2\"] = GPT2Model # Direct instance\nAutoModel.registry[\"t5\"] = \"transformers:T5Model\" # Lazy import string\n\n# 3. Bulk registration via .registry.update() - useful for many models\nAutoModel.registry.update({\n \"roberta\": RobertaModel,\n \"albert\": \"transformers:AlbertModel\",\n \"electra\": \"transformers:ElectraModel\",\n})\n\n# Auto-detect and load\nloaded = AutoModel.from_pretrained(\"./path\") # Detects type from config\n```\n\n## Why?\n\n**Before:**\n```python\n# All imports happen upfront\nfrom heavy_module_1 import ClassA\nfrom heavy_module_2 import ClassB\nfrom heavy_module_3 import ClassC\n\nREGISTRY = {\"a\": ClassA, \"b\": ClassB, \"c\": ClassC}\n```\n\n**After:**\n```python\n# Import only what you use\nfrom lazyregistry import Registry\n\nregistry = Registry(name=\"components\")\nregistry.register(\"a\", \"heavy_module_1:ClassA\")\nregistry.register(\"b\", \"heavy_module_2:ClassB\")\nregistry.register(\"c\", \"heavy_module_3:ClassC\")\n\n# Only ClassA is imported\ncomponent = registry[\"a\"]\n```\n\n## Testing\n\nRun tests with coverage:\n\n```bash\nuv run pytest tests/ --cov=lazyregistry --cov-report=term-missing\n```\n\nThe test suite includes:\n- **Core registry tests** - LazyImportDict, Registry, Namespace functionality\n- **Pretrained tests** - save/load patterns, AutoRegistry, custom state\n- **Example tests** - Verify all examples run correctly\n\n## License\n\nMIT\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A lightweight Python library for lazy-loading registries with namespace support and type safety.",
"version": "0.3.2",
"project_urls": {
"Changelog": "https://github.com/milkclouds/lazyregistry/releases",
"Homepage": "https://github.com/milkclouds/lazyregistry",
"Issues": "https://github.com/milkclouds/lazyregistry/issues",
"Repository": "https://github.com/milkclouds/lazyregistry"
},
"split_keywords": [
"lazy-loading",
" namespace",
" plugin",
" pretrained",
" registry",
" type-safe"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "91bcd98d745177640302c1c0fc8a37d365b7477d5bf2d5ebc2a4365505b1cb86",
"md5": "87738271e8140c240d75ff9ee998bbd3",
"sha256": "4300ca022ab47f5324d6c83d73ddf596b17af60d69ec808a7d9b3c4d3839a365"
},
"downloads": -1,
"filename": "lazyregistry-0.3.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "87738271e8140c240d75ff9ee998bbd3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 9761,
"upload_time": "2025-11-05T12:40:15",
"upload_time_iso_8601": "2025-11-05T12:40:15.902998Z",
"url": "https://files.pythonhosted.org/packages/91/bc/d98d745177640302c1c0fc8a37d365b7477d5bf2d5ebc2a4365505b1cb86/lazyregistry-0.3.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "d13b48a1f0f4d4b2643c93de7885a69b00f9982efce7994fc0e9f6c857e71774",
"md5": "73cfd751653e9fe1ee21b28f867573f0",
"sha256": "500d9f970c7ba2d17808a4b9d58eb50d2f19e7e139a23bef43f4fb362dfd735e"
},
"downloads": -1,
"filename": "lazyregistry-0.3.2.tar.gz",
"has_sig": false,
"md5_digest": "73cfd751653e9fe1ee21b28f867573f0",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 15873,
"upload_time": "2025-11-05T12:40:17",
"upload_time_iso_8601": "2025-11-05T12:40:17.087476Z",
"url": "https://files.pythonhosted.org/packages/d1/3b/48a1f0f4d4b2643c93de7885a69b00f9982efce7994fc0e9f6c857e71774/lazyregistry-0.3.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-05 12:40:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "milkclouds",
"github_project": "lazyregistry",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "lazyregistry"
}