Source code for planemo.cli

"""The module describes a CLI framework extending ``click``."""

import functools
import os
import sys
from typing import (
    Any,
    Callable,
    Dict,
    List,
)

import click
from click.core import (
    Command,
    Context,
)

from planemo import __version__
from planemo.context import (
    configure_standard_planemo_logging,
    PlanemoContext,
)
from planemo.exit_codes import ExitCodeException
from planemo.galaxy import profiles
from .config import OptionSource

CONTEXT_SETTINGS = dict(auto_envvar_prefix="PLANEMO")
COMMAND_ALIASES = {
    "l": "lint",
    "o": "open",
    "t": "test",
    "s": "serve",
}


[docs] class PlanemoCliContext(PlanemoContext): """Describe context of Planemo CLI computation. Extend PlanemoContext with operations for CLI concerns (exit code) and for interacting with the click library. """ def _log_message(self, message: str) -> None: click.echo(message, file=sys.stderr)
[docs] def exit(self, exit_code: int): """Exit planemo with the supplied exit code.""" self.vlog("Exiting planemo with exit code [%d]" % exit_code) raise ExitCodeException(exit_code)
pass_context = click.make_pass_decorator(PlanemoCliContext, ensure=True) cmd_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), "commands"))
[docs] def list_cmds() -> List[str]: """List planemo commands from commands folder.""" rv = [] for filename in os.listdir(cmd_folder): if filename.endswith(".py") and filename.startswith("cmd_"): rv.append(filename[len("cmd_") : -len(".py")]) rv.sort() return rv
[docs] def name_to_command(name: str) -> Command: """Convert a subcommand name to the cli function for that command. Command <X> is defined by the method 'planemo.commands.cmd_<x>:cli', this method uses `__import__` to load and return that method. """ try: mod_name = "planemo.commands.cmd_" + name mod = __import__(mod_name, None, None, ["cli"]) except ImportError as e: raise Exception(f"Problem loading command {name}, exception {e}") return mod.cli
class PlanemoCLI(click.MultiCommand): def list_commands(self, ctx: Context) -> List[str]: return list_cmds() def get_command(self, ctx: Context, name: str) -> Command: if name in COMMAND_ALIASES: name = COMMAND_ALIASES[name] return name_to_command(name)
[docs] def command_function(f: Callable) -> Callable: """Extension point for processing kwds after click callbacks.""" @functools.wraps(f) def handle_blended_options(*args, **kwds): profile = kwds.get("profile", None) if profile: ctx = args[0] profile_defaults = profiles.ensure_profile(ctx, profile, **kwds) _setup_profile_options(ctx, profile_defaults, kwds) try: return f(*args, **kwds) except ExitCodeException as e: sys.exit(e.exit_code) return pass_context(handle_blended_options)
def _setup_profile_options(ctx: PlanemoCliContext, profile_defaults: Dict[str, Any], kwds: Dict[str, Any]) -> None: for key, value in profile_defaults.items(): option_present = key in kwds option_cli_specified = option_present and (ctx.get_option_source(key) == OptionSource.cli) use_profile_option = not option_present or not option_cli_specified if use_profile_option: kwds[key] = value ctx.set_option_source(key, OptionSource.profile, force=True) @click.command(cls=PlanemoCLI, context_settings=CONTEXT_SETTINGS) @click.version_option(__version__) @click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode.") @click.option( "--config", default="~/.planemo.yml", envvar="PLANEMO_GLOBAL_CONFIG_PATH", help="Planemo configuration YAML file." ) @click.option("--directory", default="~/.planemo", envvar="PLANEMO_GLOBAL_WORKSPACE", help="Workspace for planemo.") @pass_context def planemo(ctx, config, directory, verbose, configure_logging=True): """A command-line toolkit for building tools and workflows for Galaxy. Check out the full documentation for Planemo online http://planemo.readthedocs.org or open with ``planemo docs``. All the individual planemo commands support the ``--help`` option, for example use ``planemo lint --help`` for more details on checking tools. """ ctx.verbose = verbose if configure_logging: configure_standard_planemo_logging(verbose) ctx.planemo_config = os.path.expanduser(config) ctx.planemo_directory = os.path.expanduser(directory) __all__ = ( "command_function", "list_cmds", "name_to_command", "planemo", "PlanemoCliContext", )