Source code for luci.cli

# -*- coding: utf-8 -*-
"""Console script for luci."""
import logging
import sys

import click
import click_completion
import click_log

from . import vars_file
from .cli_lucifier import CliLucifier
from .defaults import *
from .finders import LucifierFinder
from .exceptions import NoSuchDictletException
from frutils.defaults import KEY_DICTLET_LUCIFIER_NAME

# needs to be done here, otherwise there might be a circular import...
# noinspection ES6UnusedImports

log = logging.getLogger("lucify")
click_log.basic_config(log)

# optional shell completion
click_completion.init()


[docs]class LuciCommand(click.MultiCommand): """Class to create and wrap around any :class:`~luci.lucifiers.Lucifier`, with added features to generate a command-line interface for each of them.. This class extends :class:`click.MultiCommand`. It uses a special Lucifier, :class:`~luci.cli_lucifier.CliLucifier` to hold the Lucifier, investigate it's metadata and create the cli in a way click understands. """ def __init__(self, **kwargs): super(LuciCommand, self).__init__(**kwargs) self.lucifier_finder = LucifierFinder()
[docs] def list_commands(self, ctx): """Lists all registered Lucifiers. `stevedore <https://docs.openstack.org/stevedore/latest/>`_ is used to load the registered plugins. In order to register a plugin, you need to create a class that extends :class:`~luci.lucifiers.Lucifier`, and you needs to specify it like below in a packages' ``setup.py``:: entry_points={ 'luci.lucifiers': [ 'metadata=luci.lucifiers:MetadataLucifier' ] } """ result = self.lucifier_finder.get_all_dictlets().keys() return sorted(result)
[docs] def get_command(self, ctx, name): """Returns the :meth:`~luci.lucifiers.Lucifier.process` function of a wrapped Lucifier object.""" try: log.debug("loading lucifier: {}".format(name)) dictlet_path = None try: lucifier_details = self.lucifier_finder.get_dictlet_details(name) except (NoSuchDictletException) as e: log.debug("Could not load lucifier '{}', trying file...".format(name)) if not os.path.exists(name): raise click.ClickException( "Could not determine which lucifier to use for '{}'".format( name ) ) # TODO check for folder lucifier_names = self.list_commands(ctx) lucifier_to_use = None with open(name) as dictlet: for line in dictlet: if KEY_DICTLET_LUCIFIER_NAME in line: for ln in lucifier_names: if ln in line: lucifier_to_use = ln break if not lucifier_to_use: raise click.ClickException( "Could not determine with lucifier to use for dictlet '{}'".format( name ) ) log.debug("Found lucifier to use: {}".format(lucifier_to_use)) dictlet_path = name name = lucifier_to_use lucifier_details = self.lucifier_finder.get_dictlet_details(name) log.debug(" -> lucifier details: {}".format(lucifier_details)) lucifier = CliLucifier(ctx=ctx, dictlet=dictlet_path) lucifier.overlay_dictlet(name, lucifier_details, add_dictlet=True) lucifier_command = lucifier.process() if not len(lucifier_command) == 1: raise Exception("Need exactly 1 command to proceed.") cmd = list(lucifier_command.values())[0] if dictlet_path: return cmd() else: return cmd except (click.ClickException) as e: raise e except Exception as e: log.exception("Error loading dictlet '{}'".format(name)) click.echo("Could not load dictlet '{}': {}".format(name, e)) return None
VARS_HELP = ( "variables to be used for templating, can be overridden by cli options if applicable" ) DEFAULTS_HELP = "default variables, can be used to seed a lucifying process" KEEP_METADATA_HELP = "keep metadata in result directory, mostly useful for debugging" # click.core.SUBCOMMAND_METAVAR = 'LUCIFIER [LUCIFIER_PARAMS]...' @click.command(cls=LuciCommand, epilog=LUCI_EPILOG_TEXT, subcommand_metavar="LUCIFIER") @click.option( "--defaults", "-d", required=False, multiple=True, help=DEFAULTS_HELP, type=vars_file, metavar="VARS", ) @click_log.simple_verbosity_option(log, "--verbosity") @click.pass_context def cli(ctx, defaults): """luci is a generic, plug-able command-line metadata extractor and interpreter. It is build upon the `luci <https://github.com/makkus/luci` Python library. It's interface works via sub-commands (similar to git), each first-level sub-command being called a 'lucifier', which determines the task that is to be executed. The second-level sub-command is called a 'dictlet' (usually a file, but could also be a folder or even url), which contains some sort of metadata (either directly via yaml/json text content, or it's structure, or some other way). This metadata can be used directly, and/or to generate additional, dictlet-specific command-line options so users can add more metadata at run-time. The final metadata which used in the task is a merged dictionary of all metadata that was added to the dictlet (e.g. via the ``luci --defaults`` option, or command-line input to the lucifier or dictlet, or other means). Find a list of available lucifiers below. """ pass if __name__ == "__main__": sys.exit(cli()) # pragma: no cover