1

Intro

It is a strange thing that in pallets click module options can parse a command option in the '--option=value' format, where value is a string, and that is equivalent to '--option value' where again value is a string, but that when you initialize the shell completion within your click code-base using '--option=<TAB><TAB>' will not work, while '--option <TAB><TAB>' will generate completions.

I wrote a command line interface where one option takes a very string and splits it into a dictionary. The string is parsed as comma separated key value pairs where the key and the value are separated by an equal sign. The command line interface is a database driven ics file generator, that stores events that you add to it using entries like that below:

 beadroll add --alarms="alarm=last minute, trigger=-1m, summary=get ready, alarm=five minutes, trigger=-5m, summary=steady time, alarm=feeder, trigger=-15m, summary=start preparations, alarm=soul, trigger=-30m, summary=preparations" \
"Lunch with Rabbit" \
"2023-10-15T01:45:00-04:00" \
"2023-10-15T01:44:00-04:00"

the entire project is hosted on github at tunnelthrutime/beadroll. It is functional but without completion functions which is what I;m currently building. Naturally writing out long string as that above is tiring, and I'd like to make it as easy as possible. The stumbling block has been getting completion to work without the whitespace as a trigger. This has been challenging.

What I've tried

I've build a 'Custom Type Completion' and tried it to my --uid option for the time being.

class EnvVarType(click.ParamType):
    name = "envvar"
    def shell_complete(self, ctx, param, incomplete):
        return [
            click.shell_completion.CompletionItem(name)
            for name in os.environ if name.startswith(incomplete)
        ]



    def resolve_completer(self, ctx, incomplete):
        if ctx.args:
            # Get the last argument from partial_args
            last_arg = ctx.args[-1]
            if last_arg.startswith("--") and "=" in last_arg:
                # Split the argument into option name and value
                option, value = last_arg.split("=")
                if ctx.command.get_params(ctx)[option].type is self:
                    # Trigger completion based on the value
                    return self.shell_complete(ctx, None, value)

        return super().resolve_completer(ctx, incomplete)

@cli.command()
@click.option('--alarms', type=EnvVarType(), default=None)

so that with the class and the decorator it currently completes some env variables that I thought could fill in the space until I got the completion working how I wanted. So I haven't gotten a firm understanding of how to generate the triggers for the completion. I would like to have the --alarms="\<TAB\> generate a completion and then each subsequent , character work as a trigger until the double quote is closed.

It doesn't seem that my resolve_completer function is tied to my shell_complete function, or that the ctx.args are being triggered as I would like.

I'm wondering if anyone has written something similar to this and knows that proper way to initialize a trigger. I have seen implementations where programmers have used list comprehension to iterate through the params within the scope of the command but I'm not certain how I could tie that sort of looping into the behaviour of the shell_completion function. Even when I attempt if statements, such as,

class EnvVarType(click.ParamType):
    name = "envvar"
    def shell_complete(self, ctx, param, incomplete):
        if "=" in click.Context(click.Command('add')).args:
            return [
                click.shell_completion.CompletionItem(name)
                for name in ['alarm=', 'trigger=', 'summary='] if name.startswith(incomplete)
            ]
        elif "=" in ctx.args:
            return [
                click.shell_completion.CompletionItem(name)
                for name in os.environ if name.startswith(incomplete)
            ]
        else:
            return [
                click.shell_completion.CompletionItem(name)
                for name in ['alarm=', 'trigger=', 'summary=', 'other='] if name.startswith(incomplete)
            ]

the do not affect the fundamental action where pressing tab after typing --uid= removes all six characters while pressing it after --uid triggers the completion.

Perhaps when I've taken a break from it I will be able to see where I went wrong. One way that it could work is if I rewrote the basecommand to accept two options and wrote them to assign to the same value pair later. Something along the lines of this answer

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.