Source code for testplan.common.utils.registry

"""Provides Registry mapping utility."""
from testplan.common.utils import logger


[docs]class Registry(logger.Loggable): """ A utility that provides a decorator (`@registry.bind`) for mapping objects to another (decorated) class. Supports absolute or category based defaults via `@registry.bind_default` decorator as well. Example: >>> registry = Registry() >>> class ClassA: ... pass >>> # instances of ClassA are now bound to ClassB for this registry >>> @registry.bind(ClassA) >>> class ClassB: ... pass >>> obj_a = ClassA() >>> registry[obj_a] is ClassB ... True """ def __init__(self): self.data = {} self._default = None self._category_defaults = {} super(Registry, self).__init__() @property def default(self): return self._default @default.setter def default(self, value): if self._default is not None: raise ValueError( "Cannot re-bind default value. (Existing: {})".format( self.default ) ) self._default = value
[docs] def get_lookup_key(self, obj): """ This method is used for generating the key when do a lookup from the registry. Object class is used by default. """ return obj.__class__
[docs] def get_record_key(self, obj): """ This method is used for generating the key when we bind an object (possibly a class) via the registry. """ return obj
[docs] def get_category(self, obj): """ Override this to define logic for generating the category key from the object instance. """ try: return getattr(obj, "category", obj["category"]) except KeyError: # User has registered defaults for a category # however category retrieval from object failed # Need to fail explicitly and warn the user if self._category_defaults: raise NotImplementedError( "Could not retrieve category information from: {}." "You may need to override `get_category`" "of the registry.".format(obj) ) raise
def _get_default(self, obj): try: return self._category_defaults[self.get_category(obj)] except KeyError: if self._default: return self._default raise KeyError("No mapping found for: {}".format(obj)) def __getitem__(self, item): try: return self.data[self.get_lookup_key(item)] except KeyError: return self._get_default(item) def __setitem__(self, key, value): key = self.get_record_key(key) self.data[key] = value
[docs] def bind(self, *classes): """ Decorator for binding one or more classes to another. :param classes: One or more classes that will be bound to the decorated class. """ def wrapper(value): for kls in classes: self[kls] = value return value return wrapper
[docs] def bind_default(self, category=None): """ Decorator for binding a class as category based or absolute default. :param category: (optional) If provided, the decorated class will be the default for the given category, otherwise it will be the absolute default. """ def wrapper(value): if category: if category in self._category_defaults: raise ValueError( "Cannot overwrite default value " "for category: {}".format(category) ) self._category_defaults[category] = value else: self.default = value return value return wrapper