View on GitHub


25 mins
Test Coverage
import abc
import os

from salve import paths, with_metaclass
from salve.api import Block

from salve.exceptions import BlockException

class CoreBlock(with_metaclass(abc.ABCMeta, Block)):
    A block is the basic unit of configuration.
    Typically, blocks describe files, SALVE manifests, patches, etc
    This is an ABC that defines the common characteristics of all
    blocks in the SALVE Core.
    def __init__(self, ty, file_context):
        Base CoreBlock constructor.

            An element of CoreBlock.types, the type of the block.

            The FileContext of this Block. Used to pass globals and
            state information to and from the block.
        self.block_type = ty
        self.file_context = file_context
        # every block holds a hashmap of attribute names to values,
        # always initialized as empty
        self.attrs = {}
        # this is a set of attribute identifiers which always carry
        # a path as their value
        self.path_attrs = set()
        # this is a set of attribute identifiers which must be present
        # in order for the block to be valid
        self.min_attrs = set()
        # the primary attribute of a block is the one handled outside of the
        # typically block body parsing, following the block identifier
        self.primary_attr = None

    def __setitem__(self, attribute_name, value):
        Set an attribute of the block to have a specific value. Note
        that this is a destructive overwrite if the attribute had a
        previous value.

            The attribute's identifier, typically converted to lower
            The value being assigned to the attribute. Typically a
        self.attrs[attribute_name] = value

    def __getitem__(self, attribute_name):
        Return the value of a given attribute of the block.

            The attribute's identifier. Note that this is case
        return self.attrs[attribute_name]

    def __contains__(self, attribute_name):
        Checks if the block has a value associated with a given
        attribute. Returns the T/F value of that check.

            The attribute's identifier. Note that this is case
        return attribute_name in self.attrs

    def ensure_has_attrs(self, *args):
        Given a list of attributes, checks if the block has each of
        those attributes, and raises a BlockException on the first one
        that fails.

            A variable argument list of attribute identifiers to be
        for attr in args:
            if attr not in self:
                raise self.mk_except('Block(ty=' + self.block_type + ') ' +
                                     'missing attr "' + attr + '"')

    def mk_except(self, msg):
        Create a BlockException from the block. Used to easily pass the
        block's context to the exception, and to give any extra
        information that might be desirable for a specific block type.

            The string message that should be reported by the raised
        exc = BlockException(msg, self.file_context)
        return exc

    def expand_file_paths(self, root_dir):
        Expands all relative paths in a block's set of attribute values,
        given a directory to act as the prefix to all of the paths. The
        attributes are identified as paths based on the block type.

            The directory to be used as a prefix to all relative paths
            in the block.
        # define a helper to expand attributes with the root_dir
        def expand_attr(attrname):
            val = self[attrname]
            if not paths.is_abs_or_var(val):
                self[attrname] = os.path.join(root_dir, val)

        # find the minimal set of path attributes
        min_path_attrs = self.min_attrs.intersection(self.path_attrs)
        # ... and the non-minimal path attributes
        non_min_path_attrs = self.path_attrs.difference(min_path_attrs)

        # first ensure that the minimal attributes are in place

        # and expand each one
        for attr in min_path_attrs:

        # then ensure that any of the non-minimal ones that are present
        # are also expanded
        for attr in non_min_path_attrs:
            if attr in self: