Skip to content

Commit

Permalink
Add shell completion support via argcomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
scop committed Mar 26, 2023
1 parent 265841f commit aeb0a04
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 27 deletions.
14 changes: 13 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2537,6 +2537,17 @@ The two modes, `--pretty=all` (default for terminal) and `--pretty=none` (defaul
In the future, the command line syntax and some of the `--OPTIONS` may change slightly, as HTTPie improves and new features are added.
All changes are recorded in the [change log](#change-log).
### Shell completion
Shell completion is provided using the argcomplete library. It is suggested
to load the completion without falling back to the shell defaults in order
to avoid default completions in contexts where they do not apply. For example
for bash:
```bash
$ eval "$(register-python-argcomplete --no-defaults http https)"
```
### Community and Support
HTTPie has the following community channels:
Expand All @@ -2549,10 +2560,11 @@ HTTPie has the following community channels:
#### Dependencies
Under the hood, HTTPie uses these two amazing libraries:
Under the hood, HTTPie uses these three amazing libraries:
- [Requests](https://requests.readthedocs.io/en/latest/) — Python HTTP library for humans
- [Pygments](https://pygments.org/) — Python syntax highlighter
- [argcomplete](https://github.com/kislyuk/argcomplete) — Shell completion generator
#### HTTPie friends
Expand Down
1 change: 1 addition & 0 deletions httpie/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# PYTHON_ARGCOMPLETE_OK
"""The main entry point. Invoke as `http' or `python -m httpie'.
"""
Expand Down
53 changes: 28 additions & 25 deletions httpie/cli/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import textwrap
from argparse import FileType

from argcomplete.completers import ChoicesCompleter, FilesCompleter

from httpie import __doc__, __version__
from httpie.cli.argtypes import (KeyValueArgType, SessionNameValidator,
SSLCredentials, readable_file_arg,
Expand Down Expand Up @@ -64,7 +66,8 @@
$ http example.org hello=world # => POST
""",
)
).completer = ChoicesCompleter(
('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
positional_arguments.add_argument(
dest='url',
metavar='URL',
Expand All @@ -79,7 +82,7 @@
$ http :/foo # => http://localhost/foo
""",
)
).completer = ChoicesCompleter(())
positional_arguments.add_argument(
dest='request_items',
metavar='REQUEST_ITEM',
Expand Down Expand Up @@ -136,7 +139,7 @@
field-name-with\:colon=value
""",
)
).completer = ChoicesCompleter(())

#######################################################################
# Content type.
Expand Down Expand Up @@ -190,7 +193,7 @@
'Specify a custom boundary string for multipart/form-data requests. '
'Only has effect only together with --form.'
)
)
).completer = ChoicesCompleter(())
content_types.add_argument(
'--raw',
short_help='Pass raw request data without extra processing.',
Expand Down Expand Up @@ -351,7 +354,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
--response-charset=big5
""",
)
).completer = ChoicesCompleter(())
output_processing.add_argument(
'--response-mime',
metavar='MIME_TYPE',
Expand All @@ -364,7 +367,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
--response-mime=text/xml
""",
)
).completer = ChoicesCompleter(())
output_processing.add_argument(
'--format-options',
action='append',
Expand All @@ -389,7 +392,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
f' {option}' for option in DEFAULT_FORMAT_OPTIONS
).strip()
),
)
).completer = ChoicesCompleter(())

#######################################################################
# Output options
Expand Down Expand Up @@ -418,7 +421,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
response body is printed by default.
""",
)
).completer = ChoicesCompleter(())
output_options.add_argument(
'--headers',
'-h',
Expand Down Expand Up @@ -492,7 +495,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
dest='output_options_history',
metavar='WHAT',
help=Qualifiers.SUPPRESS,
)
).completer = ChoicesCompleter(())
output_options.add_argument(
'--stream',
'-S',
Expand Down Expand Up @@ -526,7 +529,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
printed to stderr.
""",
)
).completer = FilesCompleter()

output_options.add_argument(
'--download',
Expand Down Expand Up @@ -597,7 +600,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
https://httpie.io/docs/cli/config-file-directory
""",
)
).completer = FilesCompleter(('json',))
sessions.add_argument(
'--session-read-only',
metavar='SESSION_NAME_OR_PATH',
Expand All @@ -608,7 +611,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
exchange.
""",
)
).completer = FilesCompleter(('json',))

#######################################################################
# Authentication
Expand Down Expand Up @@ -672,7 +675,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
(-a username), HTTPie will prompt for the password.
""",
)
).completer = ChoicesCompleter(())
authentication.add_argument(
'--auth-type',
'-A',
Expand All @@ -683,7 +686,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
cache=False,
short_help='The authentication mechanism to be used.',
help_formatter=format_auth_help,
)
).completer = ChoicesCompleter(())
authentication.add_argument(
'--ignore-netrc',
default=False,
Expand Down Expand Up @@ -717,7 +720,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
and $HTTPS_proxy are supported as well.
""",
)
).completer = ChoicesCompleter(())
network.add_argument(
'--follow',
'-F',
Expand All @@ -735,7 +738,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
By default, requests have a limit of 30 redirects (works with --follow).
""",
)
).completer = ChoicesCompleter(())
network.add_argument(
'--max-headers',
type=int,
Expand All @@ -744,7 +747,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
'The maximum number of response headers to be read before '
'giving up (default 0, i.e., no limit).'
)
)
).completer = ChoicesCompleter(())

network.add_argument(
'--timeout',
Expand All @@ -761,7 +764,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
the underlying socket for timeout seconds).
""",
)
).completer = ChoicesCompleter(())
network.add_argument(
'--check-status',
default=False,
Expand Down Expand Up @@ -811,7 +814,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
variable instead.)
""",
)
).completer = ChoicesCompleter(('yes', 'no'))
ssl.add_argument(
'--ssl',
dest='ssl_version',
Expand All @@ -825,7 +828,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
are shown here).
""",
)
).completer = ChoicesCompleter(())
ssl.add_argument(
'--ciphers',
short_help='A string in the OpenSSL cipher list format.',
Expand All @@ -837,7 +840,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
{DEFAULT_SSL_CIPHERS}
""",
)
).completer = ChoicesCompleter(())
ssl.add_argument(
'--cert',
default=None,
Expand All @@ -849,7 +852,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
specify --cert-key separately.
""",
)
).completer = FilesCompleter(('crt', 'cert', 'pem'))
ssl.add_argument(
'--cert-key',
default=None,
Expand All @@ -860,7 +863,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
certificate file does not contain the private key.
""",
)
).completer = FilesCompleter(('key', 'pem'))

ssl.add_argument(
'--cert-key-pass',
Expand All @@ -872,7 +875,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
is given and the key file requires a passphrase.
If not provided, you’ll be prompted interactively.
"""
)
).completer = ChoicesCompleter(())

#######################################################################
# Troubleshooting
Expand Down Expand Up @@ -914,7 +917,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
'--default-scheme',
default='http',
short_help='The default scheme to use if not specified in the URL.'
)
).completer = ChoicesCompleter(('http', 'https'))
troubleshooting.add_argument(
'--debug',
action='store_true',
Expand Down
3 changes: 3 additions & 0 deletions httpie/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import socket
from typing import List, Optional, Union, Callable

import argcomplete
import requests
from pygments import __version__ as pygments_version
from requests import __version__ as requests_version
Expand Down Expand Up @@ -73,6 +74,8 @@ def handle_generic_error(e, annotation=None):

exit_status = ExitStatus.SUCCESS

argcomplete.autocomplete(parser)

try:
parsed_args = parser.parse_args(
args=args,
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
'multidict>=4.7.0',
'setuptools',
'importlib-metadata>=1.4.0; python_version < "3.8"',
'rich>=9.10.0'
'rich>=9.10.0',
'argcomplete'
]
install_requires_win_only = [
'colorama>=0.2.4',
Expand Down

0 comments on commit aeb0a04

Please sign in to comment.