我们从Python开源项目中,提取了以下43个代码示例,用于说明如何使用click.Option()。
def get_freckles_option_set(): """Helper method to create some common cli options.""" freckle_option = click.Option(param_decls=["--freckle", "-f"], required=False, multiple=True, type=RepoType(), metavar=FRECKLE_ARG_METAVAR, help=FRECKLE_ARG_HELP) target_option = click.Option(param_decls=["--target", "-t"], required=False, multiple=False, type=str, metavar=TARGET_ARG_METAVAR, help=TARGET_ARG_HELP) include_option = click.Option(param_decls=["--include", "-i"], help=INCLUDE_ARG_HELP, type=str, metavar=INCLUDE_ARG_METAVAR, default=[], multiple=True) exclude_option = click.Option(param_decls=["--exclude", "-e"], help=EXCLUDE_ARG_HELP, type=str, metavar=EXCLUDE_ARG_METAVAR, default=[], multiple=True) ask_become_pass_option = click.Option(param_decls=["--ask-become-pass", "-pw"], help=ASK_PW_HELP, type=ASK_PW_CHOICES, default="true") params = [freckle_option, target_option, include_option, exclude_option, ask_become_pass_option] return params
def generate_man_page(ctx, version=None): """ Generate documentation for the given command. :param click.Context ctx: the click context for the cli application. :rtype: str :returns: the generate man page from the given click Context. """ # Create man page with the details from the given context man_page = ManPage(ctx.command_path) man_page.version = version man_page.short_help = ctx.command.short_help man_page.description = ctx.command.help man_page.synopsis = ' '.join(ctx.command.collect_usage_pieces(ctx)) man_page.options = [x.get_help_record(None) for x in ctx.command.params if isinstance(x, click.Option)] commands = getattr(ctx.command, 'commands', None) if commands: man_page.commands = [(k, v.short_help) for k, v in commands.items()] return str(man_page)
def get_click_options(self): import click import q2cli import q2cli.core name = '--' + self.cli_name type = click.Path(exists=True, file_okay=True, dir_okay=False, readable=True) type = q2cli.core.MultipleType(type) help = ('Metadata file or artifact viewable as metadata. This ' 'option may be supplied multiple times to merge metadata.') if self.default is None: requirement = '[optional]' else: requirement = '[required]' option = q2cli.Option([name], type=type, help=help, multiple=True) yield self._add_description(option, requirement)
def get_click_options(self): import q2cli name = '--' + self.cli_name type = str help = ('Category from metadata file or artifact viewable as ' 'metadata.') if self.default is None: requirement = '[optional]' else: requirement = '[required]' option = q2cli.Option([name], type=type, help=help) yield from self.metadata_handler.get_click_options() yield self._add_description(option, requirement)
def extract_option_object(option): """Convert a click.option call into a click.Option object. Parameters ---------- option : decorator A click.option decorator. Returns ------- option_object : click.Option The option object that this decorator will create. """ @option def opt(): pass return opt.__click_params__[0]
def hidden_option(*param_decls, **attrs): """Attaches a hidden option to the command. All positional arguments are passed as parameter declarations to :class:`Option`; all keyword arguments are forwarded unchanged. This is equivalent to creating an :class:`Option` instance manually and attaching it to the :attr:`Command.params` list. """ import inspect from click.decorators import _param_memo def decorator(f): if 'help' in attrs: attrs['help'] = inspect.cleandoc(attrs['help']) _param_memo(f, HiddenOption(param_decls, **attrs)) return f return decorator #~~ helper for settings context options
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 set_help_from_yaml(cls, ctx, param, value): """ When attaching this function as `callback` argument to an Option (`click.Option`), it will set an automatic help for all Options of the same command, which do not have an `help` specified and are found in the default config file for downloading (currently `download.yaml`). The Option having as callback this function must also have `is_eager=True`. Example: Assuming opt1, opt2, opt3 are variables of the config yaml file, and opt4 not, this sets the default help for opt1 and opt2:
\@click.Option('--opt1', ..., callback=set_help_from_yaml, is_eager=True,...) \@click.Option('--opt2'...) \@click.Option('--opt3'..., help='my custom help do not set the config help') \@click.Option('--opt4'...) ... ``` """ cfg_doc = cls.DEFAULTDOC for option in (opt for opt in ctx.command.params if opt.param_type_name == 'option'): if option.help is None: option.help = cfg_doc.get(option.name, None) return value
```
def download(configfile, dburl, eventws, start, end, dataws, min_sample_rate, traveltimes_model, timespan, update_metadata, retry_url_err, retry_mseed_err, retry_seg_not_found, retry_client_err, retry_server_err, retry_timespan_err, inventory, eventws_query_args): """Download waveform data segments with quality metadata and relative events, stations and channels metadata into a specified database. The -c option (required) sets the defaults for all other options below, which are optional. The argument 'eventws_query_args' is an optional list of space separated key and values to be passed to the event web service query (example: minmag 5.5 minlon 34.5). All FDSN query arguments are valid except 'start', 'end' (set them via -t0 and -t1) and 'format' """ try: cfg_dict = yaml_load(configfile, **{k: v for k, v in locals().items() if v not in ((), {}, None, configfile)}) # start and end might be integers. If we attach the conversion function # `clickutils.valid_date` to the relative clikc Option 'type' argument, the # function does not affect integer values in the config. Thus we need to set it here: cfg_dict['start'] = clickutils.valid_date(cfg_dict['start']) cfg_dict['end'] = clickutils.valid_date(cfg_dict['end']) ret = main.download(isterminal=True, **cfg_dict) sys.exit(ret) except KeyboardInterrupt: # this except avoids printing traceback sys.exit(1) # exit with 1 as normal python exceptions
def test_command_multi_registration(basicApp): def _test_command(arg): print(arg) plugin = basicApp.plugins.get("CommandPlugin") with pytest.raises(CommandExistException): plugin.commands.register("test", "my test command", _test_command, params=[Option(("--arg", "-a"))]) plugin.commands.unregister("test") plugin.commands.register("test", "my test command", _test_command, params=[Option(("--arg", "-a"))]) assert len(basicApp.commands.get()) == 1 plugin.commands.register("test2", "my test2 command", _test_command, params=[Option(("--arg", "-a"))]) assert len(basicApp.commands.get()) == 2 basicApp.plugins.deactivate(["CommandPlugin"]) print(basicApp.commands.get().keys()) assert len(basicApp.commands.get().keys()) == 0
def test_command_multi_plugin_registration(basicApp, EmptyCommandPlugin): def _test_command(arg): print(arg) plugin = basicApp.plugins.get("CommandPlugin") plugin2 = EmptyCommandPlugin(app=basicApp, name="CommandPlugin2") plugin2.activate() plugin2.commands.register("test2", "my test2 command", _test_command, params=[Option(("--arg", "-a"))]) assert len(basicApp.commands.get()) == 2 assert len(plugin.commands.get()) == 1 assert len(plugin2.commands.get()) == 1 basicApp.plugins.deactivate(["CommandPlugin2"]) assert len(basicApp.commands.get()) == 1 assert len(plugin.commands.get()) == 1 assert len(plugin2.commands.get()) == 0
def convert_param_to_option(self, parameter): """ Convert a Parameter into a click Option. :type parameter: valohai_yaml.objs.Parameter :rtype: click.Option """ assert isinstance(parameter, Parameter) option = click.Option( param_decls=[ '--%s' % parameter.name.replace('_', '-'), ], required=(parameter.default is None and not parameter.optional), default=parameter.default, help=parameter.description, type=self.parameter_type_map.get(parameter.type, click.STRING), ) option.name = '~%s' % parameter.name # Tildify so we can pick these out of kwargs easily return option
def convert_input_to_option(self, input): """ Convert an Input into a click Option. :type input: valohai_yaml.objs.input.Input :rtype: click.Option """ assert isinstance(input, Input) option = click.Option( param_decls=[ '--%s' % input.name.replace('_', '-'), ], required=(input.default is None and not input.optional), default=input.default, metavar='URL', help='Input "%s"' % humanize_identifier(input.name), ) option.name = '^%s' % input.name # Caretize so we can pick these out of kwargs easily return option
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 generate_cli(spec): origin_url = None if isinstance(spec, str): if spec.startswith('https://') or spec.startswith('http://'): origin_url = spec r = requests.get(spec) r.raise_for_status() spec = yaml.safe_load(r.text) else: with open(spec, 'rb') as fd: spec = yaml.safe_load(fd.read()) spec = sanitize_spec(spec) cli = clickclick.AliasedGroup(context_settings=CONTEXT_SETTINGS) spec = Spec.from_dict(spec, origin_url=origin_url) for res_name, res in spec.resources.items(): grp = clickclick.AliasedGroup(normalize_command_name(res_name), short_help='Manage {}'.format(res_name)) cli.add_command(grp) for op_name, op in res.operations.items(): name = get_command_name(op) cmd = click.Command(name, callback=partial(invoke, op=op), short_help=op.op_spec.get('summary')) for param_name, param in op.params.items(): if param.required: arg = click.Argument([param.name]) cmd.params.append(arg) else: arg = click.Option(['--' + param.name]) cmd.params.append(arg) grp.add_command(cmd) return cli
def _locate_value(self, arguments, fallback, multiple=False): """Default lookup procedure to find a click.Option provided by user""" # TODO revisit this interaction between _locate_value, single vs. # multiple options, and fallbacks. Perhaps handlers should always # use tuples to store values, even for single options, in order to # normalize single-vs-multiple option handling. Probably not worth # revisiting until there are more unit + integration tests of q2cli # since there's the potential to break things. # Is it in args? v = arguments[self.click_name] missing_value = () if multiple else None if v != missing_value: return v # Does our fallback know about it? if fallback is not None: try: fallback_value = fallback(self.name, self.cli_name) except ValueNotFoundException: pass else: # TODO fallbacks don't know whether they're handling a single # vs. multiple option, so the current expectation is that # fallbacks will always return a single value. Revisit this # expectation in the future; perhaps fallbacks should be aware # of single-vs-multiple options, or perhaps they could always # return a tuple. if multiple: fallback_value = (fallback_value,) return fallback_value # Do we have a default? if self.default is not NoDefault: return self.default # Give up self.missing.append(self.cli_name) raise ValueNotFoundException()
def get_click_options(self): import q2cli # `is_flag` will set the default to `False`, but `self._locate_value` # needs to distinguish between the presence or absence of the flag # provided by the user. yield q2cli.Option( ['--' + self.cli_name], is_flag=True, default=None, help='Display verbose output to stdout and/or stderr during ' 'execution of this action. [default: %s]' % self.default)
def get_click_options(self): import q2cli # `is_flag` will set the default to `False`, but `self._locate_value` # needs to distinguish between the presence or absence of the flag # provided by the user. yield q2cli.Option( ['--' + self.cli_name], is_flag=True, default=None, help='Silence output if execution is successful ' '(silence is golden). [default: %s]' % self.default)
def get_click_options(self): import click import q2cli yield q2cli.Option( ['--' + self.cli_name], type=click.Path(exists=False, dir_okay=True, file_okay=False, writable=True), help='Output unspecified results to a directory')
def get_click_options(self): import click import q2cli yield q2cli.Option( ['--' + self.cli_name], type=click.Path(exists=True, dir_okay=False, file_okay=True, readable=True), help='Use config file for command options')
def _error_with_duplicate_in_set(self, elements): import click import collections counter = collections.Counter(elements) dups = {name for name, count in counter.items() if count > 1} ctx = click.get_current_context() click.echo(ctx.get_usage() + '\n', err=True) click.secho("Error: Option --%s was given these values: %r more than " "one time, values passed should be unique." % (self.cli_name, dups), err=True, fg='red', bold=True) ctx.exit(1)
def get_click_options(self): import q2cli import q2cli.core type = q2cli.core.ResultPath(repr=self.repr, exists=True, file_okay=True, dir_okay=False, readable=True) if self.default is None: requirement = '[optional]' else: requirement = '[required]' option = q2cli.Option(['--' + self.cli_name], type=type, help="") yield self._add_description(option, requirement)
def get_click_options(self): import q2cli type = q2cli.core.ResultPath(self.repr, exists=False, file_okay=True, dir_okay=False, writable=True) option = q2cli.Option(['--' + self.cli_name], type=type, help="") yield self._add_description( option, '[required if not passing --output-dir]')
def get_click_parameters(self): # Handlers may provide more than one click.Option for handler in self.generated_handlers.values(): yield from handler.get_click_options() # Meta-Handlers' Options: yield from self.output_dir_handler.get_click_options() yield from self.cmd_config_handler.get_click_options() yield from self.verbose_handler.get_click_options() yield from self.quiet_handler.get_click_options()
def test_cls_override(self): with self.assertRaisesRegex(ValueError, 'override `cls=q2cli.Option`'): q2cli.option('--bar', cls=click.Option)
def option(*param_decls, **attrs): """``@click.option`` decorator with customized behavior for q2cli. See docstring on ``q2cli.Option`` (above) for details. """ if 'cls' in attrs: raise ValueError("Cannot override `cls=q2cli.Option` in `attrs`.") attrs['cls'] = Option def decorator(f): return click.option(*param_decls, **attrs)(f) return decorator
def type_cast_value(self, ctx, value): # get the result of a normal type_cast converted_val = super(OneUseOption, self).type_cast_value(ctx, value) # if the option takes arguments (multiple was set to true) # assert no more than one argument was gotten, and if an argument # was gotten, take it out of the tuple and return it if self.multiple: if len(converted_val) > 1: raise click.BadParameter( "Option used multiple times.", ctx=ctx) if len(converted_val): return converted_val[0] else: return None # if the option was a flag (converted to a count) assert that the flag # count is no more than one, and type cast back to a bool elif self.count: if converted_val > 1: raise click.BadParameter( "Option used multiple times.", ctx=ctx) return bool(converted_val) else: raise ValueError(("Internal error, OneUseOption expected either " "multiple or count, but got neither."))
def one_use_option(*args, **kwargs): """ Wrapper of the click.option decorator that replaces any instances of the Option class with the custom OneUseOption class """ # cannot force a multiple or count option to be single use if "multiple" in kwargs or "count" in kwargs: raise ValueError("Internal error, one_use_option cannot be used " "with multiple or count.") # cannot force a non Option Paramater (argument) to be a OneUseOption if kwargs.get("cls"): raise TypeError("Internal error, one_use_option cannot overwrite " "cls {}.".format(kwargs.get("cls"))) # use our OneUseOption class instead of a normal Option kwargs["cls"] = OneUseOption # if dealing with a flag, switch to a counting option, # and then assert if the count is not greater than 1 and cast to a bool if kwargs.get("is_flag"): kwargs["is_flag"] = False # mutually exclusive with count kwargs["count"] = True # if not a flag, this option takes an argument(s), switch to a multiple # option, assert the len is 1, and treat the first element as the value else: kwargs["multiple"] = True # decorate with the click.option decorator, but with our custom kwargs def decorator(f): return click.option(*args, **kwargs)(f) return decorator
def confdir_defaults(ctx, param, value): for p in ctx.command.params: if isinstance(p, click.Option) and p.name.endswith('dir') and p != param: p.default = os.path.join(value, SUBDIRS[p.name]) return value
def threshold_range(ctx, param, value): for p in ctx.command.params: if isinstance(p, click.Option) and p.name == 'threshold': p.type.max = value return value
def _format_option(opt): """Format the output for a `click.Option`.""" opt = _get_help_record(opt) yield '.. option:: {}'.format(opt[0]) if opt[1]: yield '' for line in statemachine.string2lines( opt[1], tab_width=4, convert_whitespace=True): yield _indent(line)
def _format_options(ctx): """Format all `click.Option` for a `click.Command`.""" # the hidden attribute is part of click 7.x only hence use of getattr params = [x for x in ctx.command.params if isinstance(x, click.Option) and not getattr(x, 'hidden', False)] for param in params: for line in _format_option(param): yield line yield ''
def _format_envvar(param): """Format the envvars of a `click.Option` or `click.Argument`.""" yield '.. envvar:: {}'.format(param.envvar) yield '' if isinstance(param, click.Argument): param_ref = param.human_readable_name else: # if a user has defined an opt with multiple "aliases", always use the # first. For example, if '--foo' or '-f' are possible, use '--foo'. param_ref = param.opts[0] yield _indent('Provide a default for :option:`{}`'.format(param_ref))
def __init__(self, command_dir, *args, **kwargs): cs = dict(help_option_names=['-h', '--help']) params = [ click.Option( param_decls=['-v', '--verbose'], count=True, help='Make commands verbose, use ' 'multiple time for higher verbosity' ), click.Option( param_decls=['--log-file'], help='When using the verbose flag, redirects ' 'output to this file' ) ] super(AerisCLI, self).__init__(context_settings=cs, params=params, *args, **kwargs) self.command_dir = command_dir no_setup = 'AC_NO_ASSISTANT' in os.environ \ and os.environ['AC_NO_ASSISTANT'] == '1' if not no_setup and sys.stdout.isatty() and not config.complete(): click.echo(''' It seems it is the first time you are launching AerisCloud, or new configuration options were added since the last update. We can guide you through a series of questions to setup your configuration. ''', err=True) if not click.confirm('Run configuration assistant', default=True): return from .config import assistant assistant()
def _options(): """Collect all command line options""" opts = sys.argv[1:] return [click.Option((v.split('=')[0],)) for v in opts if v[0] == '-' and v != '--help']
def click_options(ctx): ''' Build and return a dictionary with the variable name from click and the associated option used from the command line, for each available option defined by the click.option decorator ie. {'rootkey':'root'} :param ctx: click context object :return: dictionary ''' return {opt.human_readable_name: opt.opts[1] for opt in ctx.command.get_params(ctx) if isinstance(opt, click.Option) and len(opt.opts) > 1}
def trait_to_option(name, trait): if is_trait(trait): if isinstance(trait, Int): return click.Option(param_decls=('--' + name,), default=trait.default_value, type=click.IntRange(trait.min, trait.max)) elif isinstance(trait, Float): return click.Option(param_decls=('--' + name,), default=trait.default_value, type=float) elif isinstance(trait, Complex): return click.Option(param_decls=('--' + name,), default=trait.default_value, type=complex) elif isinstance(trait, Bool): return click.Option(param_decls=('--' + name,), default=trait.default_value, is_flag=True) elif isinstance(trait, Unicode): return click.Option(param_decls=('--' + name,), default=trait.default_value, type=str) elif isinstance(trait, Enum): # FIXME: trait.values should be strings return click.Option(param_decls=('--' + name,), default=str(trait.default_value), type=click.Choice(list(map(str, trait.values)))) elif isinstance(trait, Tuple): return click.Option(param_decls=('--' + name,), default=trait.default_value, type=tuple( trait_to_type(t) for t in trait._traits)) else: raise InvalidValue('Trait conversion is not supported for: ' '{}'.format(trait)) else: raise InvalidType('Trait should be instance of {}'.format(TraitType))
def activate(self): self.commands.register("test", "my test command", self._test_command, params=[Option(("--arg", "-a"))])
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 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 create_command(self, provider_name): config = node_manager.valid_node_types['providers'].get(provider_name) if not config: return provider_options = [ click.Option(param_decls=['--name'], help='Compute node name', required=True) ] for param, param_data in node_manager.get_node_params(provider_name).items(): argument_params = dict() argument_params['help'] = '' description = param_data.get('description', {}).get(DEFAULT_LANG) default = param_data.get('default') arg_type = param_data.get('type') if description: argument_params['help'] += '{} '.format(description) if arg_type: argument_params['help'] += '(type: {}) '.format(arg_type) if default: argument_params.update({ 'default': default, 'required': False }) argument_params['help'] += '(default: {}) '.format(default) else: argument_params['required'] = True provider_options.append(click.Option( param_decls=['--{}'.format(param)], help=argument_params['help'], default=default, required=False if default else True )) cmd = click.Command( name=provider_name, params=provider_options, help=click.style(config['description'], fg='cyan'), short_help=click.style(config['description'], fg='cyan'), callback=self.create_node_callback ) return cmd