Skip to content

Instantly share code, notes, and snippets.

@tempusfrangit
Created August 28, 2017 23:39
Show Gist options
  • Save tempusfrangit/0b6000f71c7898ed9de12605873bf959 to your computer and use it in GitHub Desktop.
Save tempusfrangit/0b6000f71c7898ed9de12605873bf959 to your computer and use it in GitHub Desktop.
new dependency code (meta)
class _NoDependencyFound(Exception):
"""Internal-use only exception for the registry."""
class DuplicateProviderError(Exception):
"""A duplicate provider API is attempting to be registered."""
class DependencyRegistry(object):
__registry = {}
__instances = {}
__iter__ = __registry.__iter__
def __init__(self):
raise RuntimeError('Dependency Registry may not be instantiated '
'directly')
@classmethod
def get(cls, name):
# TODO(morgan) Check if keystone is configured and error if not.
registry = cls.__registry
instances = cls.__instances
try:
if name.endswith('_api'):
if name in registry:
return instances.setdefault(name, registry[name]())
except KeyError:
raise _NoDependencyFound()
@classmethod
def register_class_as_provider(cls, name, provider_cls):
if name in cls.__registry:
raise DuplicateProviderError(
'`%s` is already registered as a provider.' % name)
cls.__registry[name] = provider_cls
@classmethod
def _clear_registry(cls):
"""Used for testing purposes only.
Clears both the registry *and* the instantiations."""
# NOTE(morgan): This should *never* be used
# TODO(morgan): Delete this method once it is certain it is not needed
cls.__registry.clear()
cls.__instances.clear()
@classmethod
def _clear_registry_instances(cls):
"""Clears the provider instances and allows re-instantiation.
This is usually only used in the case of testing.
"""
cls.__instances.clear()
@classmethod
def instantiate_all_providers(cls):
for name, prov in cls.__registry.items():
cls.__instances[name] = prov()
class _DependencyRegistryMeta(type):
def __new__(meta, name, bases, class_dict):
setattr_method = None
parts = [p.lower for p in re.split(r'([A-Z][a-z]*)', name) if p]
registry_name = '_'.join(parts)
if not name.endswith('_api'):
raise ValueError('Class must have "API" as the last three '
'letters of it\'s name to be a provider api.')
if '__setattr__' in class_dict:
setattr_method = class_dict['setattr']
class_dict['_original_setattr'] = class_dict.pop('__setattr__')
def __replacement_setattr__(self, key, value):
if key in DependencyRegistry:
raise ValueError('Cannot set the name of a registered '
'provider api on a consumer.')
if setattr_method is not None:
self._original_setattr(key, value)
else:
super(self.__class__, self).__setattr__(key, value)
class_dict['__setattr__'] = __replacement_setattr__
# TODO(morgan): replace __init__ and error if the class is
# instantiated more than once.
cls = type.__new__(meta, name, bases, class_dict)
DependencyRegistry.register_class_as_provider(registry_name, cls)
return cls
def requires_api(*dependencies):
def wrapped(cls):
cls._dependencies = dependencies
__getattribute_meth = getattr(cls, '__getattribute__', None)
if __getattribute_meth is None or not callable(__getattribute_meth):
__getattribute_meth = None
def getattribute_method(self, item):
try:
return DependencyRegistry.get(item)
except _NoDependencyFound:
# NOTE(morgan): if it's not an API that is found just move on
# and do the normal thing
pass
if __getattribute_meth is not None:
return __getattribute_meth(item)
else:
super(self.__class__, self).__getattribute(item)
def init_method(self, *args, **kwargs):
for dep in self._dependencies:
if dep not in DependencyRegistry:
raise UnresolvableDependencyException(dep, [])
self._wrapped_init(*args, **kwargs)
cls._wrapped_getattribute = cls.__getattribute__
cls._wrapped_init = cls.__init__
cls.__getattribute__ = getattribute_method
cls.__init__ = init_method
return cls
return wrapped
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment