Skip to content

Commit 67be753

Browse files
authored
bump version, merge pull request #19 from iterative/devel
2 parents 82cf362 + 3018fc7 commit 67be753

File tree

8 files changed

+235
-109
lines changed

8 files changed

+235
-109
lines changed

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ Most of the magic lives in [`shtab/__init__.py`](./shtab/__init__.py).
2020
- `complete_bash()`
2121
- `complete_zsh()`
2222
- ...
23-
- `Optional()`, `Required()`, `Choice()` - helpers for advanced completion
24-
(e.g. dirs, files, `*.txt`)
23+
- `add_argument_to()` - convenience function for library integration
24+
- `Optional()`, `Required()`, `Choice()` - legacy helpers for advanced completion (e.g. dirs, files, `*.txt`)
2525
- [`main.py`](./shtab/main.py)
2626
- `get_main_parser()` - returns `shtab`'s own parser object
2727
- `main()` - `shtab`'s own CLI application
2828

2929
Given that the number of completions a program may need would likely be less
3030
than a million, the focus is on readability rather than premature speed
31-
optimisations.
31+
optimisations. The generated code itself, on the other had, should be fast.
3232

3333
Helper functions such as `replace_format` allows use of curly braces `{}` in
3434
string snippets without clashing between python's `str.format` and shell

README.rst

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,22 @@ First run ``brew install bash-completion``, then add the following to
8383
Usage
8484
-----
8585

86+
There are two ways of using ``shtab``:
87+
88+
- `CLI Usage`_: ``shtab``'s own CLI interface for external applications
89+
90+
- may not require any code modifications whatsoever
91+
- end-users execute ``shtab your_cli_app.your_parser_object``
92+
93+
- `Library Usage`_: as a library integrated into your CLI application
94+
95+
- adds a couple of lines to your application
96+
- argument mode: end-users execute ``your_cli_app --print-completion {bash,zsh}``
97+
- subparser mode: end-users execute ``your_cli_app completion {bash,zsh}``
98+
99+
CLI Usage
100+
---------
101+
86102
The only requirement is that external CLI applications provide an importable
87103
``argparse.ArgumentParser`` object (or alternatively an importable function
88104
which returns a parser object). This may require a trivial code change.
@@ -203,19 +219,24 @@ appropriate (e.g. ``$CONDA_PREFIX/etc/conda/activate.d/env_vars.sh``).
203219
By default, ``shtab`` will silently do nothing if it cannot import the requested
204220
application. Use ``-u, --error-unimportable`` to noisily complain.
205221

206-
Advanced Configuration
207-
----------------------
222+
Library Usage
223+
-------------
208224

209225
See the `examples/ <https://github.com/iterative/shtab/tree/master/examples>`_
210226
folder for more.
211227

212228
Complex projects with subparsers and custom completions for paths matching
213229
certain patterns (e.g. ``--file=*.txt``) are fully supported (see
230+
`examples/customcomplete.py <https://github.com/iterative/shtab/tree/master/examples/customcomplete.py>`_
231+
or even
214232
`iterative/dvc:command/completion.py <https://github.com/iterative/dvc/blob/master/dvc/command/completion.py>`_
215233
for example).
216234

217235
Add direct support to scripts for a little more configurability:
218236

237+
argparse
238+
~~~~~~~~
239+
219240
.. code:: python
220241
221242
#!/usr/bin/env python
@@ -224,12 +245,7 @@ Add direct support to scripts for a little more configurability:
224245
225246
def get_main_parser():
226247
parser = argparse.ArgumentParser(prog="pathcomplete")
227-
parser.add_argument(
228-
"-s",
229-
"--print-completion-shell",
230-
choices=["bash", "zsh"],
231-
help="prints completion script",
232-
)
248+
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
233249
# file & directory tab complete
234250
parser.add_argument("file", nargs="?").complete = shtab.FILE
235251
parser.add_argument("--dir", default=".").complete = shtab.DIRECTORY
@@ -238,13 +254,7 @@ Add direct support to scripts for a little more configurability:
238254
if __name__ == "__main__":
239255
parser = get_main_parser()
240256
args = parser.parse_args()
241-
242-
# completion magic
243-
shell = args.print_completion_shell
244-
if shell:
245-
print(shtab.complete(parser, shell=shell))
246-
else:
247-
print("received <file>=%r --dir=%r" % (args.file, args.dir))
257+
print("received <file>=%r --dir=%r" % (args.file, args.dir))
248258
249259
docopt
250260
~~~~~~
@@ -262,8 +272,6 @@ object from `docopt <https://pypi.org/project/docopt>`_ syntax:
262272
263273
Options:
264274
-g, --goodbye : Say "goodbye" (instead of "hello")
265-
-b, --print-bash-completion : Output a bash tab-completion script
266-
-z, --print-zsh-completion : Output a zsh tab-completion script
267275
268276
Arguments:
269277
<you> : Your name [default: Anon]
@@ -272,15 +280,9 @@ object from `docopt <https://pypi.org/project/docopt>`_ syntax:
272280
import sys, argopt, shtab # NOQA
273281
274282
parser = argopt.argopt(__doc__)
283+
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
275284
if __name__ == "__main__":
276285
args = parser.parse_args()
277-
if args.print_bash_completion:
278-
print(shtab.complete(parser, shell="bash"))
279-
sys.exit(0)
280-
if args.print_zsh_completion:
281-
print(shtab.complete(parser, shell="zsh"))
282-
sys.exit(0)
283-
284286
msg = "k thx bai!" if args.goodbye else "hai!"
285287
print("{} says '{}' to {}".format(args.me, msg, args.you))
286288

examples/customcomplete.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
"""
3-
`argparse`-based CLI app with custom file completion.
3+
`argparse`-based CLI app with custom file completion as well as subparsers.
44
55
See `pathcomplete.py` for a more basic version.
66
"""
@@ -25,14 +25,24 @@
2525
}
2626

2727

28-
def get_main_parser():
29-
parser = argparse.ArgumentParser(prog="customcomplete")
30-
parser.add_argument(
31-
"-s",
32-
"--print-completion-shell",
33-
choices=["bash", "zsh"],
34-
help="prints completion script",
28+
def process(args):
29+
print(
30+
"received <input_txt>=%r --input-file=%r --output-name=%r"
31+
% (args.input_txt, args.input_file, args.output_name)
3532
)
33+
34+
35+
def get_main_parser():
36+
main_parser = argparse.ArgumentParser(prog="customcomplete")
37+
subparsers = main_parser.add_subparsers()
38+
# make required (py3.7 API change); vis. https://bugs.python.org/issue16308
39+
subparsers.required = True
40+
subparsers.dest = "subcommand"
41+
42+
parser = subparsers.add_parser("completion")
43+
shtab.add_argument_to(parser, "shell") # magic!
44+
45+
parser = subparsers.add_parser("process")
3646
# `*.txt` file tab completion
3747
parser.add_argument("input_txt", nargs="?").complete = TXT_FILE
3848
# file tab completion builtin shortcut
@@ -45,20 +55,11 @@ def get_main_parser():
4555
" accidentally overwriting existing files."
4656
),
4757
).complete = shtab.DIRECTORY # directory tab completion builtin shortcut
48-
return parser
58+
parser.set_defaults(func=process)
59+
return main_parser
4960

5061

5162
if __name__ == "__main__":
5263
parser = get_main_parser()
5364
args = parser.parse_args()
54-
55-
# completion magic
56-
shell = args.print_completion_shell
57-
if shell:
58-
script = shtab.complete(parser, shell=shell, preamble=PREAMBLE)
59-
print(script)
60-
else:
61-
print(
62-
"received <input_txt>=%r --output-dir=%r --output-name=%r"
63-
% (args.input_txt, args.output_dir, args.output_name)
64-
)
65+
args.func(args)

examples/docopt-greeter.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
77
Options:
88
-g, --goodbye : Say "goodbye" (instead of "hello")
9-
-b, --print-bash-completion : Output a bash tab-completion script
10-
-z, --print-zsh-completion : Output a zsh tab-completion script
119
1210
Arguments:
1311
<you> : Your name [default: Anon]
@@ -16,14 +14,9 @@
1614
import sys, argopt, shtab # NOQA
1715

1816
parser = argopt.argopt(__doc__)
17+
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
1918
if __name__ == "__main__":
2019
args = parser.parse_args()
21-
if args.print_bash_completion:
22-
print(shtab.complete(parser, shell="bash"))
23-
sys.exit(0)
24-
if args.print_zsh_completion:
25-
print(shtab.complete(parser, shell="zsh"))
26-
sys.exit(0)
2720

2821
msg = "k thx bai!" if args.goodbye else "hai!"
2922
print("{} says '{}' to {}".format(args.me, msg, args.you))

examples/pathcomplete.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,7 @@
1212

1313
def get_main_parser():
1414
parser = argparse.ArgumentParser(prog="pathcomplete")
15-
parser.add_argument(
16-
"-s",
17-
"--print-completion-shell",
18-
choices=["bash", "zsh"],
19-
help="prints completion script",
20-
)
15+
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
2116
# file & directory tab complete
2217
parser.add_argument("file", nargs="?").complete = shtab.FILE
2318
parser.add_argument("--dir", default=".").complete = shtab.DIRECTORY
@@ -27,10 +22,4 @@ def get_main_parser():
2722
if __name__ == "__main__":
2823
parser = get_main_parser()
2924
args = parser.parse_args()
30-
31-
# completion magic
32-
shell = args.print_completion_shell
33-
if shell:
34-
print(shtab.complete(parser, shell=shell))
35-
else:
36-
print("received <file>=%r --dir=%r" % (args.file, args.dir))
25+
print("received <file>=%r --dir=%r" % (args.file, args.dir))

0 commit comments

Comments
 (0)