Ejemplo n.º 1
0
def user_configuration(
    config_directory: Optional[Path] = None,
) -> Tuple[
    AstralityYAMLConfigDict,
    Dict[str, 'ModuleConfigDict'],
    Context,
    Path,
]:
    """
    Return instantiation parameters for ModuleManager.

    :return: Tuple containing: astrality.yml dictionary, global modules
        dictionary, global context dictionary, and path to config directory.
    """
    config_directory, config_file = infer_config_location(config_directory)

    # First get global context, which we can use when compiling other files
    context_file = config_directory / 'context.yml'
    if context_file.exists():
        global_context = Context(utils.compile_yaml(
            path=context_file,
            context=Context(),
        ))
    else:
        global_context = Context()

    # Global configuration options
    config: AstralityYAMLConfigDict = utils.compile_yaml(  # type: ignore
        path=config_file,
        context=global_context,
    )

    # Insert default global settings that are not specified
    for section_name in ('astrality', 'modules',):
        section_content = config.get(section_name, {})
        config[section_name] = ASTRALITY_DEFAULT_GLOBAL_SETTINGS[section_name].copy()  # type: ignore # noqa
        config[section_name].update(section_content)  # type: ignore

    # Globally defined modules
    modules_file = config_directory / 'modules.yml'
    if modules_file.exists():
        modules = utils.compile_yaml(
            path=modules_file,
            context=global_context,
        )
    else:
        modules = {}

    return config, modules, global_context, config_directory
Ejemplo n.º 2
0
def dummy_config():
    """Return dummy configuration YAML file."""
    test_conf = Path(__file__).parents[1] / 'test_config' / 'test.yml'
    return compile_yaml(
        path=test_conf,
        context=Context(),
    )
Ejemplo n.º 3
0
 def test_dict_from_config_file(self, dir_with_compilable_files):
     """Placeholders should be properly substituted."""
     config = compile_yaml(
         path=dir_with_compilable_files / 'astrality.yml',
         context={},
     )
     assert config == {
         'key1': 'test_value',
         'key2': 'test',
     }
Ejemplo n.º 4
0
def test_hot_reloading(
    test_template_targets,
    test_config_directory,
):
    template_target1, template_target2 = test_template_targets
    config1 = test_config_directory / 'modules1.yml'
    config2 = test_config_directory / 'modules2.yml'
    target_config = test_config_directory / 'modules.yml'

    # Copy the first configuration into place
    shutil.copy(str(config1), str(target_config))

    modules1 = utils.compile_yaml(
        config1,
        context={},
    )

    application_config1 = {'astrality': {'hot_reload_config': True}}
    module_manager = ModuleManager(
        config=application_config1,
        modules=modules1,
        directory=test_config_directory,
    )

    # Before beginning, the template should not be compiled
    assert not template_target1.is_file()

    # But when we finalize tasks, it should be compiled
    module_manager.finish_tasks()
    assert template_target1.is_file()

    # Also check that the filewatcher has been started
    assert module_manager.directory_watcher.observer.is_alive()

    # We now "edit" the configuration file
    shutil.copy(str(config2), str(target_config))

    # Since hot reloading is enabled, the new template target should be
    # compiled, and the old one cleaned up
    retry = Retry()
    assert retry(lambda: template_target2.is_file())
    assert retry(lambda: not template_target1.is_file())

    # And we switch back again
    shutil.copy(str(config1), str(target_config))
    assert retry(lambda: template_target1.is_file())
    assert retry(lambda: not template_target2.is_file())

    # Cleanup config file
    if target_config.is_file():
        os.remove(target_config)

    # Stop the filewatcher
    module_manager.directory_watcher.stop()
Ejemplo n.º 5
0
    def context(self, context: Context = Context()) -> Context:
        """
        Return context defined in module source.

        :param context: Context used when compiling "context.yml".
        :return: Context dictionary.
        """
        if not self.context_file.exists():
            return Context()

        if hasattr(self, '_context'):
            return self._context

        self._context = Context(utils.compile_yaml(
            path=self.context_file,
            context=context,
        ))
        return self._context
Ejemplo n.º 6
0
    def import_context(
        self,
        from_path: Path,
        from_section: Optional[str] = None,
        to_section: Optional[str] = None,
    ) -> None:
        """
        Insert context values from yml file.

        :param from_path: Path to .yml file or directory containing
            "context.yml".
        :param from_section: If given, only import specific section from path.
        :param to_section: If given, rename from_section to to_section.
        """
        new_context = utils.compile_yaml(
            path=from_path,
            context=self,
        )

        logger = logging.getLogger(__name__)
        if from_section is None and to_section is None:
            logger.info(
                f'[import_context] All sections from "{from_path}".',
            )
            self.update(new_context)
        elif from_section and to_section:
            logger.info(
                f'[import_context] Section "{from_section}" from "{from_path}" '
                f'into section "{to_section}".',
            )
            self[to_section] = new_context[from_section]
        else:
            assert from_section
            logger.info(
                f'[import_context] Section "{from_section}" '
                f'from "{from_path}" ',
            )
            self[from_section] = new_context[from_section]
Ejemplo n.º 7
0
def filter_config_file(
    config_file: Path,
    context: Context,
    enabled_module_name: str,
    prepend: str,
) -> Dict[str, 'ModuleConfigDict']:
    """
    Return a filtered dictionary representing `config_file`.

    Only modules given by `enabled_module_name` are kept, and their names
    are prepended with `prepend`.
    """
    assert config_file.name == 'modules.yml'

    try:
        modules_dict = utils.compile_yaml(
            path=config_file,
            context=context,
        )
    except FileNotFoundError:
        logger.warning(
            f'Non-existent module configuration file "{config_file}" '
            'Skipping enabled module '
            f'"{prepend}{enabled_module_name}"',
        )
        return {}

    if not modules_dict:  # pragma: no cover
        logger.warning(
            f'Empty modules configuration "{config_file}".',
        )
        return {}
    elif not isinstance(modules_dict, dict):  # pragma: no cover
        logger.critical(
            f'Configuration file "{config_file}" not formated as '
            'a dictionary at root indentation.',
        )
        raise MisconfiguredConfigurationFile

    modules = tuple(modules_dict.keys())
    if enabled_module_name != '*' \
            and enabled_module_name not in modules:
        raise NonExistentEnabledModule

    # We rename each module to module/{self.name}.module_name
    # in order to prevent naming conflicts when using modules provided
    # from a third party with the same name as another managed module.
    # This way you can use a module named "conky" from two third parties,
    # in addition to providing your own.
    for module_name in modules:
        if not enabled_module_name == '*' \
           and enabled_module_name != module_name:
            # The module is not enabled, remove the module
            modules_dict.pop(module_name)
            continue

        # Replace the module name with folder_name.module_name
        non_conflicting_module_name = prepend + module_name
        module_section = modules_dict.pop(module_name)
        modules_dict[non_conflicting_module_name] = module_section

    return modules_dict
Ejemplo n.º 8
0
def dummy_config():
    test_conf = Path(__file__).parents[1] / 'test_config' / 'test.yml'
    return compile_yaml(
        path=test_conf,
        context=Context(),
    )