1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
# Usage
There are two ways of using `shtab`:
- [CLI Usage](#cli-usage): `shtab`'s own CLI interface for external applications
- may not require any code modifications whatsoever
- end-users execute `shtab your_cli_app.your_parser_object`
- [Library Usage](#library-usage): as a library integrated into your CLI application
- adds a couple of lines to your application
- argument mode: end-users execute `your_cli_app --print-completion {bash,zsh,tcsh}`
- subparser mode: end-users execute `your_cli_app completion {bash,zsh,tcsh}`
## CLI Usage
The only requirement is that external CLI applications provide an importable
`argparse.ArgumentParser` object (or alternatively an importable function which
returns a parser object). This may require a trivial code change.
Once that's done, simply put the output of `shtab --shell=your_shell
your_cli_app.your_parser_object` somewhere your shell looks for completions.
Below are various examples of enabling `shtab`'s own tab completion scripts.
!!! info
If both shtab and the module it's completing are globally importable, eager
usage is an option. "Eager" means automatically updating completions each
time a terminal is opened.
=== "bash"
```sh
shtab --shell=bash shtab.main.get_main_parser --error-unimportable \
| sudo tee "$BASH_COMPLETION_COMPAT_DIR"/shtab
```
=== "Eager bash"
There are a few options:
```sh
# Install locally
echo 'eval "$(shtab --shell=bash shtab.main.get_main_parser)"' \
>> ~/.bash_completion
# Install locally (lazy load for bash-completion>=2.8)
echo 'eval "$(shtab --shell=bash shtab.main.get_main_parser)"' \
> "${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions/shtab"
# Install system-wide
echo 'eval "$(shtab --shell=bash shtab.main.get_main_parser)"' \
| sudo tee "$(pkg-config --variable=completionsdir bash-completion)"/shtab
# Install system-wide (legacy)
echo 'eval "$(shtab --shell=bash shtab.main.get_main_parser)"' \
| sudo tee "$BASH_COMPLETION_COMPAT_DIR"/shtab
```
=== "zsh"
Note that `zsh` requires completion script files to be named `_{EXECUTABLE}`
(with an underscore prefix).
```sh
# note the underscore `_` prefix
shtab --shell=zsh shtab.main.get_main_parser --error-unimportable \
| sudo tee /usr/local/share/zsh/site-functions/_shtab
```
=== "Eager zsh"
To be more eager, place the generated script somewhere in `$fpath`. For
example, add these lines to the top of `~/.zshrc`:
```sh
mkdir -p ~/.zsh/completions
fpath=($fpath ~/.zsh/completions) # must be before `compinit` lines
shtab --shell=zsh shtab.main.get_main_parser > ~/.zsh/completions/_shtab
```
=== "tcsh"
```sh
shtab --shell=tcsh shtab.main.get_main_parser --error-unimportable \
| sudo tee /etc/profile.d/shtab.completion.csh
```
=== "Eager tcsh"
There are a few options:
```sh
# Install locally
echo 'shtab --shell=tcsh shtab.main.get_main_parser | source /dev/stdin' \
>> ~/.cshrc
# Install system-wide
echo 'shtab --shell=tcsh shtab.main.get_main_parser | source /dev/stdin' \
| sudo tee /etc/profile.d/eager-completion.csh
```
!!! tip
See the [examples/](https://github.com/iterative/shtab/tree/main/examples)
folder for more.
Any existing `argparse`-based scripts should be supported with minimal effort.
For example, starting with this existing code:
```{.py title="main.py" linenums="1" #main.py}
#!/usr/bin/env python
import argparse
def get_main_parser():
parser = argparse.ArgumentParser(prog="MY_PROG", ...)
parser.add_argument(...)
parser.add_subparsers(...)
...
return parser
if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()
...
```
Assuming this code example is installed in `MY_PROG.command.main`, simply run:
=== "bash"
```sh
shtab --shell=bash -u MY_PROG.command.main.get_main_parser \
| sudo tee "$BASH_COMPLETION_COMPAT_DIR"/MY_PROG
```
=== "zsh"
```sh
shtab --shell=zsh -u MY_PROG.command.main.get_main_parser \
| sudo tee /usr/local/share/zsh/site-functions/_MY_PROG
```
=== "tcsh"
```sh
shtab --shell=tcsh -u MY_PROG.command.main.get_main_parser \
| sudo tee /etc/profile.d/MY_PROG.completion.csh
```
## Library Usage
!!! tip
See the [examples/](https://github.com/iterative/shtab/tree/main/examples)
folder for more.
Complex projects with subparsers and custom completions for paths matching
certain patterns (e.g. `--file=*.txt`) are fully supported (see
[examples/customcomplete.py](https://github.com/iterative/shtab/tree/main/examples/customcomplete.py)
or even
[iterative/dvc:commands/completion.py](https://github.com/iterative/dvc/blob/main/dvc/commands/completion.py)
for example).
Add direct support to scripts for a little more configurability:
=== "argparse"
```{.py title="pathcomplete.py" linenums="1" hl_lines="7 9-10"}
#!/usr/bin/env python
import argparse
import shtab # for completion magic
def get_main_parser():
parser = argparse.ArgumentParser(prog="pathcomplete")
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
# file & directory tab complete
parser.add_argument("file", nargs="?").complete = shtab.FILE
parser.add_argument("--dir", default=".").complete = shtab.DIRECTORY
return parser
if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()
print("received <file>=%r --dir=%r" % (args.file, args.dir))
```
=== "docopt"
Simply use [argopt](https://pypi.org/project/argopt) to create a parser
object from [docopt](https://pypi.org/project/docopt) syntax:
```{.py title="docopt-greeter.py" linenums="1" hl_lines="17"}
#!/usr/bin/env python
"""Greetings and partings.
Usage:
greeter [options] [<you>] [<me>]
Options:
-g, --goodbye : Say "goodbye" (instead of "hello")
Arguments:
<you> : Your name [default: Anon]
<me> : My name [default: Casper]
"""
import argopt, shtab
parser = argopt.argopt(__doc__)
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
if __name__ == "__main__":
args = parser.parse_args()
msg = "k thx bai!" if args.goodbye else "hai!"
print("{} says '{}' to {}".format(args.me, msg, args.you))
```
|