我们从Python开源项目中,提取了以下19个代码示例,用于说明如何使用click.MultiCommand()。
def make_commands(section, **click_args): """Make a Click multicommand from all submodules of the module.""" class MCommand(click.MultiCommand): """Treadmill CLI driver.""" def __init__(self, *args, **kwargs): if kwargs and click_args: kwargs.update(click_args) click.MultiCommand.__init__(self, *args, **kwargs) def list_commands(self, ctx): """Return list of commands in section.""" return sorted(plugin_manager.names(section)) def get_command(self, ctx, name): try: return plugin_manager.load(section, name).init() except KeyError: raise click.UsageError('Invalid command: %s' % name) return MCommand
def __init__(self, *args, **kwargs): click.MultiCommand.__init__(self, *args, **kwargs) from octoprint.util.commandline import CommandlineCaller from functools import partial def log_util(f): def log(*lines): for line in lines: f(line) return log self.command_caller = CommandlineCaller() self.command_caller.on_log_call = log_util(lambda x: click.echo(">> {}".format(x))) self.command_caller.on_log_stdout = log_util(click.echo) self.command_caller.on_log_stderr = log_util(partial(click.echo, err=True))
def resolve_ctx(cli, prog_name, args): ctx = cli.make_context(prog_name, list(args), resilient_parsing=True) while ctx.args + ctx.protected_args and isinstance(ctx.command, MultiCommand): a = ctx.protected_args + ctx.args cmd = ctx.command.get_command(ctx, a[0]) if cmd is None: return None ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True) return ctx
def get_choices(cli, prog_name, args, incomplete): ctx = resolve_ctx(cli, prog_name, args) if ctx is None: return optctx = None if args: for param in ctx.command.get_params(ctx): if isinstance(param, Option) and not param.is_flag and args[-1] in param.opts + param.secondary_opts: optctx = param choices = [] if optctx: choices += [c if isinstance(c, tuple) else (c, None) for c in optctx.type.complete(ctx, incomplete)] elif incomplete and not incomplete[:1].isalnum(): for param in ctx.command.get_params(ctx): if not isinstance(param, Option): continue for opt in param.opts: if startswith(opt, incomplete): choices.append((opt, param.help)) for opt in param.secondary_opts: if startswith(opt, incomplete): # don't put the doc so fish won't group the primary and # and secondary options choices.append((opt, None)) elif isinstance(ctx.command, MultiCommand): for name in ctx.command.list_commands(ctx): if startswith(name, incomplete): choices.append((name, ctx.command.get_command_short_help(ctx, name))) else: for param in ctx.command.get_params(ctx): if isinstance(param, Argument): choices += [c if isinstance(c, tuple) else (c, None) for c in param.type.complete(ctx, incomplete)] for item, help in choices: yield (item, help)
def init(): """patch click to support enhanced completion""" import click click.types.ParamType.complete = param_type_complete click.types.Choice.complete = choice_complete click.core.MultiCommand.get_command_short_help = multicommand_get_command_short_help click.core._bashcomplete = _shellcomplete
def format_commands(self, ctx, formatter): rows_by_prefix = defaultdict(list) def add_commands(multicommand, prefix=''): for subcommand in multicommand.list_commands(ctx): cmd = multicommand.get_command(ctx, subcommand) assert cmd is not None rows_by_prefix[prefix.strip()].append((prefix + subcommand, (cmd.short_help or ''))) if isinstance(cmd, click.MultiCommand): add_commands(cmd, prefix + '%s ' % cmd.name) add_commands(self) for prefix, rows in sorted(rows_by_prefix.items()): title = ( 'Commands (%s ...)' % prefix if prefix else 'Commands' ) with formatter.section(title): formatter.write_dl(rows)
def __init__(self, modules_path, base_module_name, **attrs): click.MultiCommand.__init__(self, **attrs) self.base_module_name = base_module_name self.modules_path = modules_path
def __init__(self, config, **kwargs): """Base class to provide a command-based (similar to e.g. git) cli for freckles. This class parses the folders in the paths provided by the config element for so-called 'freckle adapters'. A freckle adapter is a collection of files that describe commandline-arguments and tasks lists to execute on a folder (more information: XXX) Args: config (FrecklesConfig): the config wrapper object kwargs (dict): additional arguments that are forwarded to the partent click.MultiCommand constructor """ click.MultiCommand.__init__(self, "freckles", result_callback=assemble_freckle_run, invoke_without_command=True, **kwargs) use_repo_option = click.Option(param_decls=["--use-repo", "-r"], required=False, multiple=True, help="extra context repos to use", is_eager=True, callback=download_extra_repos) output_option = click.Option(param_decls=["--output", "-o"], required=False, default="default", metavar="FORMAT", type=click.Choice(SUPPORTED_OUTPUT_FORMATS), is_eager=True, help="format of the output") no_run_option = click.Option(param_decls=["--no-run"], help='don\'t execute frecklecute, only prepare environment and print task list', type=bool, is_flag=True, default=False, required=False) version_option = click.Option(param_decls=["--version"], help='prints the version of freckles', type=bool, is_flag=True, is_eager=True, expose_value=False, callback=print_version) self.params = get_freckles_option_set() self.params.extend([ use_repo_option, output_option, no_run_option, version_option]) self.config = config self.profile_repo = ProfileRepo(self.config) # self.command_names.append(BREAK_COMMAND_NAME)
def __init__(self, current_command, config, **kwargs): """Base class to provide a command-based (similar to e.g. git) cli for frecklecute. This class parses the folders in the paths provided by the config element for so-called 'frecklecutables', (yaml) text files that contain a list of tasks and optionally command-line argument descriptions. More information: XXX Args: current_command (tuple): a tuple in the format (command_name, command_path), which is used for commands that are paths, instead of filenames in one of the known frecklecutable paths. Can be (None, None) if not a path. config (FrecklesConfig): the config wrapper object kwargs (dict): additional arguments that are forwarded to the partent click.MultiCommand constructor """ click.MultiCommand.__init__(self, "freckles", **kwargs) output_option = click.Option(param_decls=["--output", "-o"], required=False, default="default", metavar="FORMAT", type=click.Choice(SUPPORTED_OUTPUT_FORMATS), help="format of the output", is_eager=True) ask_become_pass_option = click.Option(param_decls=["--ask-become-pass", "-pw"], help='whether to force ask for a password, force ask not to, or let try freckles decide (which might not always work)', type=click.Choice(["auto", "true", "false"]), default="auto") version_option = click.Option(param_decls=["--version"], help='prints the version of freckles', type=bool, is_flag=True, is_eager=True, expose_value=False, callback=print_version) no_run_option = click.Option(param_decls=["--no-run"], help='don\'t execute frecklecute, only prepare environment and print task list', type=bool, is_flag=True, default=False, required=False) use_repo_option = click.Option(param_decls=["--use-repo", "-r"], required=False, multiple=True, help="extra context repos to use", is_eager=True, callback=download_extra_repos, expose_value=True) self.params = [use_repo_option, output_option, ask_become_pass_option, no_run_option, version_option] self.config = config # .trusted_repos = DEFAULT_FRECKLES_CONFIG.trusted_repos # local_paths = get_local_repos(trusted_repos, "frecklecutables") self.command_repo = CommandRepo(config=self.config, additional_commands=[current_command]) self.current_command = current_command[0]
def make_multi_command(module_name, **click_args): """Make a Click multicommand from all submodules of the module.""" class MCommand(click.MultiCommand): """Treadmill CLI driver.""" def __init__(self, *args, **kwargs): if kwargs and click_args: kwargs.update(click_args) click.MultiCommand.__init__(self, *args, **kwargs) def list_commands(self, ctx): climod = importlib.import_module(module_name) commands = set( [modulename for _loader, modulename, _ispkg in pkgutil.iter_modules(climod.__path__)] ) return sorted([cmd.replace('_', '-') for cmd in commands]) def get_command(self, ctx, name): try: full_name = '.'.join([module_name, name.replace('-', '_')]) mod = importlib.import_module(full_name) return mod.init() except Exception: # pylint: disable=W0703 with tempfile.NamedTemporaryFile(delete=False, mode='w') as f: traceback.print_exc(file=f) click.echo( 'Unable to load plugin: %s [ %s ]' % (name, f.name), err=True) return return MCommand
def __init__(self, *args, **kwargs): click.MultiCommand.__init__(self, *args, **kwargs) self.settings = None self.plugin_manager = None self.hooks = dict() self._logger = logging.getLogger(__name__) self._initialized = False
def _generate_command_reply(cmd): """Recursively generate completion reply for this command and subcommands. Parameters ---------- cmd : click.Command Command to generate completion replies for (including its subcommands). """ import textwrap import click ctx = None options = ['--help'] for param in cmd.params: if isinstance(param, click.Option): options.extend(param.opts) options.extend(param.secondary_opts) subcmd_names = [] if isinstance(cmd, click.MultiCommand): subcmd_names.extend(cmd.list_commands(ctx)) subcmd_cases = [] for subcmd_name in subcmd_names: subcmd_reply = _generate_command_reply( cmd.get_command(ctx, subcmd_name)) subcmd_reply = textwrap.indent(subcmd_reply, ' ') case = SUBCOMMAND_CASE_TEMPLATE.format( subcmd_name=subcmd_name, subcmd_reply=subcmd_reply) subcmd_cases.append(case) subcmd_cases = textwrap.indent('\n'.join(subcmd_cases), ' ' * 6) cmd_reply = COMMAND_REPLY_TEMPLATE.format( options=' '.join(options), subcmd_names=' '.join(subcmd_names), subcmd_cases=subcmd_cases) return cmd_reply # NOTE: using double braces to avoid `str.format` interpolation when bash needs # curly braces in the generated code. # # NOTE: the handling of a negative COMP_CWORD is necessary in certain versions # of bash (e.g. at least the bash shipped with OS X 10.9.5). When adding # whitespace to the end of a command, and then moving the cursor backwards in # the command and hitting <tab>, COMP_CWORD can be negative (I've only seen -2 # as its value). This is a bash bug and is not documented behavior. Other CLIs # with tab completion suffer from the same issue, and each one deals with this # bug differently (some not at all, e.g. `git`). The workaround used below # seems to provide the least destructive completion behavior for our CLI. # # Bug report reference: # https://lists.gnu.org/archive/html/bug-bash/2009-07/msg00108.html
def list_commands(): def _print_cmd(command): # print commands with short_help indent = 4 min_space = 2 # if the output would be pinched too close together, or if the command # name would overflow, use two separate lines if len(command.name) > _command_length - min_space: safeprint(' '*indent + command.name) safeprint(' '*(indent + _command_length) + command.short_help) # otherwise, it's all cool to cram into one line, just ljust command # names so that they form a nice column else: safeprint(' '*indent + '{}{}'.format( command.name.ljust(_command_length), command.short_help)) def _print_cmd_group(command, parent_names): parents = ' '.join(parent_names) if parents: parents = parents + ' ' safeprint('\n=== {}{} ===\n'.format(parents, command.name)) def _recursive_list_commands(command, parent_names=None): if parent_names is None: parent_names = [] # names of parent commands, including this one, for passthrough to # recursive calls new_parent_names = copy.copy(parent_names) + [command.name] # group-style commands are printed as headers if isinstance(command, click.MultiCommand): _print_cmd_group(command, parent_names) # get the set of subcommands and recursively print all of them group_cmds = [v for v in command.commands.values() if isinstance(v, click.MultiCommand)] func_cmds = [v for v in command.commands.values() if v not in group_cmds] # we want to print them all, but func commands first for cmd in (func_cmds + group_cmds): _recursive_list_commands(cmd, parent_names=new_parent_names) # individual commands are printed solo else: _print_cmd(command) # get the root context (the click context for the entire CLI tree) root_ctx = click.get_current_context().find_root() _recursive_list_commands(root_ctx.command) # get an extra newline at the end safeprint('')
def get_completion_context(args): """ Walk the tree of commands to a terminal command or multicommand, using the Click Context system. Effectively, we'll be using the resilient_parsing mode of commands to stop evaluation, then having them capture their options and arguments, passing us on to the next subcommand. If we walk "off the tree" with a command that we don't recognize, we have a hardstop condition, but otherwise, we walk as far as we can go and that's the location from which we should do our completion work. """ # get the "globus" command as a click.Command root_command = click.get_current_context().find_root().command # build a new context object off of it, with resilient_parsing set so that # no callbacks are invoked ctx = root_command.make_context('globus', list(args), resilient_parsing=True) # walk down multicommands until we've matched on everything and are at a # terminal context that holds all of our completed args while isinstance(ctx.command, click.MultiCommand) and args: # trim out any params that are capturable at this level of the command # tree by resetting the argument list args = ctx.protected_args + ctx.args # if there were no remaining args, stop walking the tree if not args: break # check for a matching command, and if one isn't found stop the # traversal and abort the whole process -- this would mean that a # completed command was entered which doesn't match a known command # there's nothing completion can do in this case unless it implements # sophisticated fuzzy matching command = ctx.command.get_command(ctx, args[0]) if not command: return None # otherwise, grab that command, and build a subcontext to continue the # tree walk else: ctx = command.make_context(args[0], args[1:], parent=ctx, resilient_parsing=True) # return the context we found return ctx
def cmd_complete(out, name, cmd, level=1): ctx = click.Context(cmd) cmpl = { 'name': name, 'cmd': cmd, 'level': level, 'flags': [opts for param in cmd.params for opts in param.opts if isinstance(param, click.Option)], 'complete_cmd': complete_cmd } params = [opts for param in cmd.params for opts in param.opts if isinstance(param, click.Parameter) and not isinstance(param, click.Option)] if isinstance(cmd, click.MultiCommand): cmds = cmd.list_commands(ctx) for cmd_name in cmds: cmd_complete( out, name + '_' + cmd_name, cmd.get_command(ctx, cmd_name), level + 1 ) cmpl['cmds'] = cmds out.write(render_cli('autocomplete-multi', **cmpl)) else: # TODO: we might want to move that list of params somewhere else completable = [ 'command', 'direction', 'endpoint', 'env', 'host', 'inventory', 'inventory_name', 'job', 'limit', 'organization_name', 'platform', 'project', 'server', ] if len(params) == 1 and params[0] in completable: cmpl['param'] = params[0] elif len(params) > 1: cmpl['params'] = [el for el in params if el in completable] out.write(render_cli('autocomplete-single', **cmpl)) if level == 1: out.write('complete -F _{0}_completion {0}\n'.format(name))
def make_manage_multi_command(module_name, **click_args): """Make a Click multicommand from all submodules of the module.""" old_multi = cli.make_multi_command(module_name, **click_args) new_multi = cli.make_commands(module_name, **click_args) class MCommand(click.MultiCommand): """Treadmill CLI driver.""" def __init__(self, *args, **kwargs): self.old_multi = old_multi(*args, **kwargs) self.new_multi = new_multi(*args, **kwargs) if kwargs and click_args: kwargs.update(click_args) click.MultiCommand.__init__(self, *args, **kwargs) def list_commands(self, ctx): old_commands = set(self.old_multi.list_commands(ctx)) new_commands = set(self.new_multi.list_commands(ctx)) return sorted(old_commands | new_commands) def get_command(self, ctx, name): try: return self.new_multi.get_command(ctx, name) except click.UsageError: pass return self.old_multi.get_command(ctx, name) def format_commands(self, ctx, formatter): rows = [] for subcommand in self.list_commands(ctx): entry_points = list(pkg_resources.iter_entry_points( module_name, subcommand)) # Try get the help with importlib if entry_point not found if len(entry_points) == 0: cmd = self.old_multi.get_command(ctx, subcommand) if cmd is None: continue rows.append((subcommand, cmd.short_help or '')) else: dist = entry_points[0].dist if dist.has_metadata('cli_help'): help_text = dist.get_metadata('cli_help') else: help_text = '' rows.append((subcommand, help_text)) if rows: with formatter.section('Commands'): formatter.write_dl(rows) return MCommand