Source code for selinonlib.config

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ######################################################################
# Copyright (C) 2016-2017  Fridolin Pokorny, fridolin.pokorny@gmail.com
# This file is part of Selinon project.
# ######################################################################
"""User configuration."""

import yaml

from selinonlib.errors import RequestError


class _ConfigSingleton(type):
    """Config singleton metaclass."""

    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(_ConfigSingleton, cls).__call__(cls._config, *args, **kwargs)
        return cls._instance

    @classmethod
    def set_config(mcs, config_path):
        """Set config which should be used in within Config singleton.

        :param config_path: a path to configuration that should be used
        :type config_path: str
        """
        # set _config before the singleton is instantiated
        assert mcs._instance is None  # nosec
        mcs._config = config_path


[docs]def style_configuration(style_name): """Adjust style based on default style and provided style configuration (if any).""" def decorator(func): """Decorate a function call.""" def wrapper(config_instance): """Wrap style configuration that makes sure style is adjusted based on user configuration.""" property_name = '_' + func.__name__ style_value = getattr(config_instance, property_name) if not style_value: style_value = getattr(config_instance, 'DEFAULT_' + func.__name__.upper()) # Style name could be directly determined from function name, but leave it this way for the # sake of readability config_style_value = (config_instance.raw_config.get('style') or {}).get(style_name, {}) style_value.update(config_style_value) setattr(config_instance, property_name, style_value) return func(config_instance) return wrapper return decorator
[docs]class Config(metaclass=_ConfigSingleton): # pylint: disable=too-many-instance-attributes """Configuration supplied by user.""" DEFAULT_STYLE_TASK = { 'style': 'filled', 'color': 'black', 'fillcolor': '#66cfa7', 'shape': 'ellipse' } DEFAULT_STYLE_FLOW = { 'style': 'filled', 'color': 'black', 'fillcolor': '#197a9f', 'shape': 'box3d' } DEFAULT_STYLE_CONDITION = { 'style': 'filled', 'color': 'gray', 'fillcolor': '#e8e3c8', 'shape': 'octagon' } DEFAULT_STYLE_CONDITION_FOREACH = { 'style': 'filled', 'color': 'gray', 'fillcolor': '#e8e3c8', 'shape': 'doubleoctagon' } DEFAULT_STYLE_STORAGE = { 'style': 'filled', 'color': 'black', 'fillcolor': '#894830', 'shape': 'cylinder' } DEFAULT_STYLE_EDGE = { 'arrowType': 'open', 'color': 'black' } DEFAULT_STYLE_STORE_EDGE = { 'arrowType': 'open', 'color': '#894830', 'style': 'dashed' } DEFAULT_STYLE_GRAPH = { } DEFAULT_STYLE_FALLBACK_EDGE = { 'arrowType': 'open', 'color': '#cc1010' } DEFAULT_STYLE_FALLBACK_TRUE = { 'style': 'filled', 'color': 'black', 'fillcolor': '#5af47b', 'shape': 'plain' } def __init__(self, config=None): """Instantiate configuration.""" self.raw_config = {} self.config_path = config # These get assigned in style_configuration decorator. self._style_task = None self._style_flow = None self._style_condition = None self._style_condition_foreach = None self._style_storage = None self._style_edge = None self._style_store_edge = None self._style_graph = None self._style_fallback_edge = None self._style_fallback_node = None self._style_fallback_true = None if self.config_path is not None: try: with open(self.config_path) as input_file: self.raw_config = yaml.load(input_file, Loader=yaml.SafeLoader) except Exception as exc: raise RequestError("Unable to load or parse style configuration file %r: %s" % (self.config_path, str(exc))) from exc @style_configuration('task')
[docs] def style_task(self): """Return style for tasks in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_task
@style_configuration('flow')
[docs] def style_flow(self): """Return style for a flow node in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_flow
@style_configuration('condition')
[docs] def style_condition(self): """Return style for conditions in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_condition
@style_configuration('condition_foreach')
[docs] def style_condition_foreach(self): """Return style for foreach edges in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_condition_foreach
@style_configuration('storage')
[docs] def style_storage(self): """Return style for storage in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_storage
@style_configuration('edge')
[docs] def style_edge(self): """Return style for edges in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_edge
@style_configuration('store_edge')
[docs] def style_store_edge(self): """Return style for edges that lead to a storage in the graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_store_edge
@style_configuration('graph')
[docs] def style_graph(self): """Return style for the whole graph, see graphviz styling options. :return: style definition :rtype: dict """ return self._style_graph
@style_configuration('fallback_edge')
[docs] def style_fallback_edge(self): """Return style for fallback edges. :return: style definition :rtype: dict """ return self._style_fallback_edge
@style_configuration('fallback_true')
[docs] def style_fallback_true(self): """Return style for fallback true node. :return: style definition :rtype: dict """ return self._style_fallback_true