Skip to content

Commit

Permalink
bump version, merge pull request #19 from iterative/devel
Browse files Browse the repository at this point in the history
  • Loading branch information
casperdcl authored Aug 4, 2020
2 parents 82cf362 + 3018fc7 commit 67be753
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 109 deletions.
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Most of the magic lives in [`shtab/__init__.py`](./shtab/__init__.py).
- `complete_bash()`
- `complete_zsh()`
- ...
- `Optional()`, `Required()`, `Choice()` - helpers for advanced completion
(e.g. dirs, files, `*.txt`)
- `add_argument_to()` - convenience function for library integration
- `Optional()`, `Required()`, `Choice()` - legacy helpers for advanced completion (e.g. dirs, files, `*.txt`)
- [`main.py`](./shtab/main.py)
- `get_main_parser()` - returns `shtab`'s own parser object
- `main()` - `shtab`'s own CLI application

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

Helper functions such as `replace_format` allows use of curly braces `{}` in
string snippets without clashing between python's `str.format` and shell
Expand Down
50 changes: 26 additions & 24 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ First run ``brew install bash-completion``, then add the following to
Usage
-----

There are two ways of using ``shtab``:

- `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`_: 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}``
- subparser mode: end-users execute ``your_cli_app completion {bash,zsh}``

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.
Expand Down Expand Up @@ -203,19 +219,24 @@ appropriate (e.g. ``$CONDA_PREFIX/etc/conda/activate.d/env_vars.sh``).
By default, ``shtab`` will silently do nothing if it cannot import the requested
application. Use ``-u, --error-unimportable`` to noisily complain.

Advanced Configuration
----------------------
Library Usage
-------------

See the `examples/ <https://github.com/iterative/shtab/tree/master/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/master/examples/customcomplete.py>`_
or even
`iterative/dvc:command/completion.py <https://github.com/iterative/dvc/blob/master/dvc/command/completion.py>`_
for example).

Add direct support to scripts for a little more configurability:

argparse
~~~~~~~~

.. code:: python
#!/usr/bin/env python
Expand All @@ -224,12 +245,7 @@ Add direct support to scripts for a little more configurability:
def get_main_parser():
parser = argparse.ArgumentParser(prog="pathcomplete")
parser.add_argument(
"-s",
"--print-completion-shell",
choices=["bash", "zsh"],
help="prints completion script",
)
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
Expand All @@ -238,13 +254,7 @@ Add direct support to scripts for a little more configurability:
if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()
# completion magic
shell = args.print_completion_shell
if shell:
print(shtab.complete(parser, shell=shell))
else:
print("received <file>=%r --dir=%r" % (args.file, args.dir))
print("received <file>=%r --dir=%r" % (args.file, args.dir))
docopt
~~~~~~
Expand All @@ -262,8 +272,6 @@ object from `docopt <https://pypi.org/project/docopt>`_ syntax:
Options:
-g, --goodbye : Say "goodbye" (instead of "hello")
-b, --print-bash-completion : Output a bash tab-completion script
-z, --print-zsh-completion : Output a zsh tab-completion script
Arguments:
<you> : Your name [default: Anon]
Expand All @@ -272,15 +280,9 @@ object from `docopt <https://pypi.org/project/docopt>`_ syntax:
import sys, argopt, shtab # NOQA
parser = argopt.argopt(__doc__)
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
if __name__ == "__main__":
args = parser.parse_args()
if args.print_bash_completion:
print(shtab.complete(parser, shell="bash"))
sys.exit(0)
if args.print_zsh_completion:
print(shtab.complete(parser, shell="zsh"))
sys.exit(0)
msg = "k thx bai!" if args.goodbye else "hai!"
print("{} says '{}' to {}".format(args.me, msg, args.you))
Expand Down
41 changes: 21 additions & 20 deletions examples/customcomplete.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
"""
`argparse`-based CLI app with custom file completion.
`argparse`-based CLI app with custom file completion as well as subparsers.
See `pathcomplete.py` for a more basic version.
"""
Expand All @@ -25,14 +25,24 @@
}


def get_main_parser():
parser = argparse.ArgumentParser(prog="customcomplete")
parser.add_argument(
"-s",
"--print-completion-shell",
choices=["bash", "zsh"],
help="prints completion script",
def process(args):
print(
"received <input_txt>=%r --input-file=%r --output-name=%r"
% (args.input_txt, args.input_file, args.output_name)
)


def get_main_parser():
main_parser = argparse.ArgumentParser(prog="customcomplete")
subparsers = main_parser.add_subparsers()
# make required (py3.7 API change); vis. https://bugs.python.org/issue16308
subparsers.required = True
subparsers.dest = "subcommand"

parser = subparsers.add_parser("completion")
shtab.add_argument_to(parser, "shell") # magic!

parser = subparsers.add_parser("process")
# `*.txt` file tab completion
parser.add_argument("input_txt", nargs="?").complete = TXT_FILE
# file tab completion builtin shortcut
Expand All @@ -45,20 +55,11 @@ def get_main_parser():
" accidentally overwriting existing files."
),
).complete = shtab.DIRECTORY # directory tab completion builtin shortcut
return parser
parser.set_defaults(func=process)
return main_parser


if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()

# completion magic
shell = args.print_completion_shell
if shell:
script = shtab.complete(parser, shell=shell, preamble=PREAMBLE)
print(script)
else:
print(
"received <input_txt>=%r --output-dir=%r --output-name=%r"
% (args.input_txt, args.output_dir, args.output_name)
)
args.func(args)
9 changes: 1 addition & 8 deletions examples/docopt-greeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
Options:
-g, --goodbye : Say "goodbye" (instead of "hello")
-b, --print-bash-completion : Output a bash tab-completion script
-z, --print-zsh-completion : Output a zsh tab-completion script
Arguments:
<you> : Your name [default: Anon]
Expand All @@ -16,14 +14,9 @@
import sys, argopt, shtab # NOQA

parser = argopt.argopt(__doc__)
shtab.add_argument_to(parser, ["-s", "--print-completion"]) # magic!
if __name__ == "__main__":
args = parser.parse_args()
if args.print_bash_completion:
print(shtab.complete(parser, shell="bash"))
sys.exit(0)
if args.print_zsh_completion:
print(shtab.complete(parser, shell="zsh"))
sys.exit(0)

msg = "k thx bai!" if args.goodbye else "hai!"
print("{} says '{}' to {}".format(args.me, msg, args.you))
15 changes: 2 additions & 13 deletions examples/pathcomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@

def get_main_parser():
parser = argparse.ArgumentParser(prog="pathcomplete")
parser.add_argument(
"-s",
"--print-completion-shell",
choices=["bash", "zsh"],
help="prints completion script",
)
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
Expand All @@ -27,10 +22,4 @@ def get_main_parser():
if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()

# completion magic
shell = args.print_completion_shell
if shell:
print(shtab.complete(parser, shell=shell))
else:
print("received <file>=%r --dir=%r" % (args.file, args.dir))
print("received <file>=%r --dir=%r" % (args.file, args.dir))
Loading

0 comments on commit 67be753

Please sign in to comment.