Created
January 13, 2013 15:56
-
-
Save bruth/4524784 to your computer and use it in GitHub Desktop.
Registry class that follows a common pattern in Django for autoloading specific modules for a given purpose, e.g. `models.py` and `admin.py`. One such use case enables registering instances or classes and making them available in the admin to associate with some data.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect | |
from django.conf import settings | |
from django.utils.importlib import import_module | |
from django.core.exceptions import ImproperlyConfigured | |
class AlreadyRegistered(Exception): | |
pass | |
class NotRegistered(Exception): | |
pass | |
class Registry(object): | |
"Simple class that keeps track of a set of registered classes." | |
def __init__(self, default=None, register_instance=True): | |
if register_instance and inspect.isclass(default): | |
default = default() | |
self.register_instance = register_instance | |
self.default = default | |
self._registry = {} | |
if default: | |
self.register(default) | |
def __getitem__(self, name): | |
return self._registry.get(name, self.default) | |
def get(self, name): | |
return self.__getitem__(name) | |
def register(self, obj, name=None): | |
"""Registers a class with an optional name. The class name will be used | |
if not supplied. | |
""" | |
if inspect.isclass(obj): | |
name = name or obj.__name__ | |
# Create an instance if instances should be registered | |
if self.register_instance: | |
obj = obj() | |
else: | |
name = name or obj.__class__.__name__ | |
if name in self._registry: | |
raise AlreadyRegistered('The class {0} is already registered'.format(name)) | |
# Check to see if this class should be used as the default for this | |
# registry | |
if getattr(obj, 'default', False): | |
# ensure the default if already overriden is not being overriden | |
# again. | |
if self.default: | |
if self.register_instance: | |
name = self.default.__class__.__name__ | |
else: | |
name = self.default.__name__ | |
objtype = 'class' if self.register_instance else 'instance' | |
raise ImproperlyConfigured('The default {0} cannot be set ' | |
'more than once for this registry ({1} is the default).'.format(objtype, name)) | |
self.default = obj | |
else: | |
if name in self._registry: | |
raise AlreadyRegistered('Another class is registered with the ' | |
'name "{0}"'.format(name)) | |
self._registry[name] = obj | |
def unregister(self, name): | |
"""Unregisters a class. Note that these calls must be made in | |
INSTALLED_APPS listed after the apps that already registered the class. | |
""" | |
# Use the name of the class if passed in. Second condition checks for an | |
# instance of the class. | |
if inspect.isclass(name): | |
name = name.__name__ | |
elif hasattr(name, '__class__'): | |
name = name.__class__.__name__ | |
if name not in self._registry: | |
objtype = 'class' if self.register_instance else 'instance' | |
raise NotRegistered('No {0} is registered under the name "{1}"'.format(objtype, name)) | |
self._registry.pop(name) | |
@property | |
def choices(self): | |
"Returns a 2-tuple list of all registered class instance names." | |
return sorted((x, x) for x in self._registry.iterkeys()) | |
def autodiscover(module_name): | |
"""Simple auto-discover for looking through each INSTALLED_APPS for each | |
``module_name`` and fail silently when not found. This should be used for | |
modules that have 'registration' like behavior. | |
""" | |
for app in settings.INSTALLED_APPS: | |
# Attempt to import the app's ``module_name``. | |
try: | |
import_module('{0}.{1}'.format(app, module_name)) | |
except: | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment