Пример #1
0
def test_all_mappings_yields_all_mappings():
    x = ClassMap()
    x[object] = 1
    x[BC] = 2
    x[B] = 3
    x[C] = 4
    x[A] = 5
    assert list(x.all_mappings(BC)) == [2, 3, 4, 5, 1]
Пример #2
0
class SpecificationMapper(object):
    """Maps descriptions of some type to a type. Has configurable handlers for
    what a description may look like. Handlers for descriptions may take either
    a specific value or all instances of a type and have access to the mapper
    to look up types.

    Also supports prototype based inheritance, with children being able to
    override specific handlers

    There is a single default() object per subclass of SpecificationMapper
    which everything has as a prototype if it's not assigned any other
    prototype. This allows you to define the mappers on the default object
    and have them inherited by any custom mappers you want.

    """
    @classmethod
    def default(cls):
        key = '_%s_default_mapper' % (cls.__name__, )
        try:
            return getattr(cls, key)
        except AttributeError:
            pass
        result = cls()
        setattr(cls, key, result)
        return result

    @classmethod
    def clear_default(cls):
        try:
            delattr(cls, '_%s_default_mapper' % (cls.__name__, ))
        except AttributeError:
            pass

    def __init__(self, prototype=None):
        self.value_mappers = {}
        self.instance_mappers = ClassMap()
        self.__prototype = prototype
        self.__descriptor_cache = {}

    def prototype(self):
        if self.__prototype:
            return self.__prototype
        if self is self.default():
            return None
        return self.default()

    def define_specification_for(self, value, specification):
        self.value_mappers.setdefault(value, []).append(specification)
        self.clear_cache()

    def clear_cache(self):
        self.__descriptor_cache = {}

    def define_specification_for_instances(self, cls, specification):
        self.instance_mappers.setdefault(cls, []).append(specification)
        self.clear_cache()

    def define_specification_for_classes(self,
                                         specification,
                                         subclasses_of=None):
        if subclasses_of:
            original_specification = specification

            @wraps(specification)
            def restricted(sms, descriptor):
                if issubclass(descriptor, subclasses_of):
                    return original_specification(sms, descriptor)
                else:
                    return next_in_chain()

            specification = restricted

        self.define_specification_for_instances(typekey(SpecificationMapper),
                                                specification)
        self.clear_cache()

    def new_child_mapper(self):
        return self.__class__(prototype=self)

    def specification_for(self, descriptor):
        k = HashItAnyway(descriptor)
        try:
            return self.__descriptor_cache[k]
        except KeyError:
            pass
        r = self._calculate_specification_for(descriptor)
        self.__descriptor_cache[k] = r
        return r

    def _calculate_specification_for(self, descriptor):
        for h in self.find_specification_handlers_for(descriptor):
            try:
                r = h(self, descriptor)
                break
            except NextInChain:
                pass
        else:
            r = self.missing_specification(descriptor)

        return r

    def has_specification_for(self, descriptor):
        try:
            self.specification_for(descriptor)
            return True
        except MissingSpecification:
            return False

    def find_specification_handlers_for(self, descriptor):
        if safe_in(descriptor, self.value_mappers):
            for h in reversed(self.value_mappers[descriptor]):
                yield h
        tk = typekey(descriptor)
        for h in self.__instance_handlers(tk):
            yield h
        if self.prototype():
            for h in self.prototype().find_specification_handlers_for(
                    descriptor):
                yield h

    def __instance_handlers(self, tk):
        for hs in self.instance_mappers.all_mappings(tk):
            for h in reversed(hs):
                yield h

    def missing_specification(self, descriptor):
        raise MissingSpecification(descriptor)