Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next release v1.3.0 #19

Merged
merged 17 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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