Let argparse reserve an attribute for completion

Can argparse.Action reserve an attribute named complete? That is, for any action:

    action = parser.add_argument("--dir", default=".")
    print(action.complete)

will get None, user can customize it by

action.complete = {"bash": "some string about bash completion", "zsh": "some string about zsh completion"}

or

    action = parser.add_argument("--dir", default="." complete={"bash": "XXX", "zsh": "XXX"})

Then let any third-party library to use action.complete to generate completion script for specific shell.

A library to allow user to convert
a parser to shell completion script, it inject complete to action by shtab.add_argument_to.

    parser = argparse.ArgumentParser()
    shtab.add_argument_to(parser, ["-s", "--print-completion"])  # magic!
    # file & directory tab complete
    parser.add_argument("--file").complete = shtab.FILE
    parser.add_argument("--dir", default=".").complete = shtab.DIRECTORY

However, it must need shtab. I think perhaps we can

def get_parser()
    parser = argparse.ArgumentParser()
    parser.add_argument("--file", complete={"zsh": "_files"}, help = "input file")
    parser.add_argument("--dir", default=".", complete={"zsh": "_dirs"}, help = "input dir", metavar="INPUT_DIR")
    parser.add_argument("--config-file", default="~/.config/foo/config.py", complete={"zsh": "_files -g '*.py'"}, help = "config file")
    return parser

Then

from foo.__main__ import get_parser
from a_library import generate_shell_completion

print(generate_shell_completion(get_parser(), shell="zsh"))

Will print

_argument -S -s \
    "--dir[input dir]:INPUT_DIR:_dirs" \
    "--file[input file]:FILE:_files" \
    "--config-file[config file]:CONFIG_FILE:_files -g '*.py'"

which is a legal zsh completion script.

This is the effect:

$ foo --<TAB>
options
--dir                 input dir
--file                input file
--config-file         config file

I think parser.add_argument("--file", complete=XXX) is more convenient than
parser.add_argument("--file").complete=XXX, and it will not break the
compatibility. (the old code will not use action.complete). And argparse will
not use action.complete: it just let any third-party library to use
action.complete to support different shells. That is, if a package developer
want to their command line program support a new shell, it need:

  1. the command line program developer use complete attribute to decide what to complete
    (such as only complete '*.py': _files -g '*.py')
  2. The third party library developer develop a package to utilize complete attribute to
    support their familiar shells, such as python -m generate_shell_completion --shell zsh foo.__main__:get_parser > the_file_name
  3. The packager of linux distributions to generate this shell completion
    the_file_name and move it to correct path such as
    /usr/share/zsh/site-functions or /usr/local/share/zsh/site-functions

The command line program developer can get rid of shtab.add_argument_to and just use complete attribute.

And moreover, the parser can also provide complete attribute like this:

def get_parser()
    parser = argparse.ArgumentParser(complete = {"zsh": "function _complete_py() { _files -g '*.py' }"})
    parser.add_argument("--config-file", default="~/.config/foo/config.py", complete={"zsh": "_complete_py"})
    parser.add_argument("--source-file", complete={"zsh": "_complete_py"})
    return parser

Which allow command line program developer to utilize the same code scratch:

function _complete_py() { _files -g '*.py' }

_argument -S -s \
    "--config-file[]:CONFIG_FILE:_complete_py" \
    "--source-file[]:SOURCE_FILE:_complete_py"

In conclusion, I hope argparse can reverse an attribute named complete.

This is just my 2C. I think it is better than https://github.com/python/cpython/issues/48506 (which want to argparse support --help-options).

Thanks to discuss!

It seems like a library can produce the desired completion scripts by walking through the parser.

It sounds similar to GitHub - kislyuk/argcomplete: Python and tab completion, better together., but argcomplete doesn’t have native support for zsh AFAIK.