Stop trying to make fetch happen.
A typed component registry backed by zope.interface. Register
components behind formal interfaces. Look them up without circular
imports. Relationships are inferred and bidirectional.
pip install beckon
For Django integration:
pip install beckon[django]
Define an interface:
from zope.interface import Interface
from beckon import BeckonInterface
class _IService(Interface):
pass
IService = BeckonInterface(_IService)Register components and look them up:
from beckon import register
register(IService, MyService, name='email')import beckon
beckon(IService, 'email') # -> MyService
beckon(IService) # -> [('email', MyService)]The module itself is callable — import beckon; beckon(...) works
without importing the function separately.
Add beckon.django to INSTALLED_APPS:
INSTALLED_APPS = [
'beckon.django',
# ...
]This does three things:
- Registers every Django model with
IModelautomatically - Registers a name resolver so models are named by
_meta.label_lower(e.g.posts.post) - Auto-discovers
resources.pyin every installed app via acq
Every model is available immediately:
import beckon
from beckon.django import IModel
Post = beckon(IModel, 'posts.post')For anything beyond models (serializers, permissions, custom
interfaces), put register() calls in a resources.py file
inside any installed app. beckon finds them automatically on
startup:
# posts/resources.py
from beckon import register
from myapp.interfaces import ISerializer
from .serializers import PostSerializer
register(ISerializer, PostSerializer) # infers model from Meta.modelfrom zope.interface import Interface
from beckon import BeckonInterface
class _ISerializer(Interface):
pass
ISerializer = BeckonInterface(_ISerializer, infer_from='model')The infer_from parameter tells beckon which attribute to check
when inferring relationships. When a component has a Meta.model
attribute pointing to a registered model, beckon links them
automatically:
# posts/resources.py
from beckon import register
from myapp.interfaces import ISerializer
from .serializers import PostSerializer
register(ISerializer, PostSerializer) # infers model from Meta.modelSince models are already registered, beckon links the serializer to its model automatically:
import beckon
beckon(ISerializer, 'posts.post') # -> PostSerializer
beckon(IModel, 'posts.post') # -> Post (reverse works too)Interfaces carry generic type information:
IModel: BeckonInterface[type[Model]] = BeckonInterface(_IModel)mypy infers return types automatically:
beckon(IModel, 'auth.user') # mypy sees: type[Model] | NoneDefine your own typed interfaces and get the same inference:
IPermission: BeckonInterface[type[BasePermission]] = BeckonInterface(_IPermission)
beckon(IPermission, 'is_admin') # mypy sees: type[BasePermission] | NoneBSD 2-Clause. See LICENSE.