Ejemplo n.º 1
0
    def mro(self):
        if not self._is_resource(self.__name__, self.__bases__):
            # Not a resource; perform the normal MRO.
            return super(ResourceBase, self).mro()

        # Retrieve the normal MRO.
        mro = super(ResourceBase, self).mro()

        # Remove all connectors from the mro.
        for cls in list(mro):
            if cls in connector_set:
                mro.remove(cls)

        # Attempt to resolve a list of connectors.
        meta = self.__dict__.get('meta')
        if meta:
            cmap = CONNECTORS
            for key, ref in six.iteritems(meta.connectors):
                if key not in self.connectors:
                    continue

                name = cmap[key]['resource'][0].format(ref)
                module = utils.import_module(name)
                if module:
                    klass = getattr(module, cmap[key]['resource'][1], None)
                    if klass:
                        # Found a connector class for this connector.

                        # Find where in the MRO it should go.
                        # It needs to go directly after the resource base
                        # for which it is a connector for.
                        for index, cls in enumerate(mro):
                            if key in getattr(cls, 'connectors', []):
                                break

                        # Let us forever know this is a connector class.
                        connector_set.update(set(klass.__mro__) - set(mro))

                        # Insert the connector class.
                        for cls in klass.__mro__:
                            if cls not in mro:
                                index += 1
                                mro.insert(index, cls)

        # Return the constructed MRO.
        return mro
Ejemplo n.º 2
0
def apply_connectors(meta, name, bases, metadata, base_meta):

    # Apply the declared connectors. What this entails is inserting the
    # connector base classes at the correct location in the base class list.

    # The correct location would be directly before one of the base resource
    # classes.

    # What is not implemented (and will not be) is replacing existing
    # connectors. You may have an inheritance chain of as many "abstract"
    # resources as warranted but as soon as a connector is supplied on a
    # non-abstract resource then that connector is "locked-in" so-to-speak.

    # Order the connectors found in meta by the order declared above.
    connectors = OrderedDict()
    for key in reversed(CONNECTORS):
        if key in meta.connectors:
            connectors[key] = meta.connectors[key]

    meta.connectors = connectors

    # Iterate through the declared connectors.
    cmap = CONNECTORS
    new_bases = list(bases)
    offset = 0
    for key, ref in six.iteritems(meta.connectors):

        try:
            # Resolve an optional options class that may be provided by
            # the connector.
            options = utils.import_module(cmap[key]['options'][0].format(ref))
            options = getattr(options, cmap[key]['options'][1])
            options_instance = options(metadata, name, base_meta)
            meta.__dict__.update(**options_instance.__dict__)

        except (AttributeError, KeyError):
            pass

        try:
            # Resolve the connector itself.
            module = utils.import_module(cmap[key]['resource'][0].format(ref))
            class_ = getattr(module, cmap[key]['resource'][1], None)

            # Ensure the connector has not already been applied.
            applied = False
            for base in bases:
                if issubclass(base, class_):
                    applied = True

            if applied:
                continue

            # Find where to insert this connector.
            module_key = 'armet.resources.%s' % cmap[key]['resource'][2]
            module = utils.import_module(module_key)
            armet_resource = getattr(module, cmap[key]['resource'][1])
            for index, base in enumerate(bases):
                if issubclass(base, armet_resource):
                    new_bases.insert(index + offset, class_)
                    offset += 1
                    break

        except (AttributeError, KeyError):
            pass

    return tuple(new_bases)
Ejemplo n.º 3
0
    def __new__(cls, name, bases, attrs):
        if not cls._is_resource(name, bases):
            # This is not an actual resource.
            return super(ResourceBase, cls).__new__(cls, name, bases, attrs)

        # Gather the attributes of all options classes.
        # Start with the base configuration.
        metadata = armet.use().copy()
        values = lambda x: {n: getattr(x, n) for n in dir(x)}

        # Expand the options class with the gathered metadata.
        base_meta = []
        cls._gather_metadata(base_meta, bases)

        # Apply the configuration from each class in the chain.
        for meta in base_meta:
            metadata.update(**values(meta))

        cur_meta = {}
        if attrs.get('Meta'):
            # Apply the configuration from the current class.
            cur_meta = values(attrs['Meta'])
            metadata.update(**cur_meta)

        # Gather and construct the options object.
        meta = attrs['meta'] = cls.options(metadata, name, cur_meta, base_meta)

        # Construct the class object.
        self = super(ResourceBase, cls).__new__(cls, name, bases, attrs)

        # Generate a serializer map that maps media ranges to serializer
        # names.
        self._serializer_map = smap = {}
        for key in self.meta.allowed_serializers:
            serializer = self.meta.serializers[key]
            for media_type in serializer.media_types:
                smap[media_type] = key

        # Generate a deserializer map that maps media ranges to deserializer
        # names.
        self._deserializer_map = dmap = {}
        for key in self.meta.allowed_deserializers:
            deserializer = self.meta.deserializers[key]
            for media_type in deserializer.media_types:
                dmap[media_type] = key

        # Filter the available connectors according to the
        # metaclass restriction set.
        for key in list(meta.connectors.keys()):
            if key not in cls.connectors:
                del meta.connectors[key]

        # Iterate through the available connectors.
        iterator = six.iteritems(meta.connectors)
        self.connectors = connectors = []
        cmap = CONNECTORS
        for key, ref in iterator:
            options = utils.import_module(cmap[key]['options'][0].format(ref))
            if options:
                options = getattr(options, cmap[key]['options'][1], None)
                if options:
                    # Available options to parse for this connector;
                    # instantiate the options class and apply all
                    # available options.
                    options_instance = options(metadata, name, base_meta)
                    meta.__dict__.update(**options_instance.__dict__)

            module = utils.import_module(cmap[key]['resource'][0].format(ref))
            if module:
                klass = getattr(module, cmap[key]['resource'][1], None)
                if klass:
                    # Found a connector class for this connector
                    connectors.append(klass)

        # Return the constructed instance.
        return self