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 Apr 29, 2023
1 parent 28d11b2 commit 8497692
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 32 deletions.
83 changes: 53 additions & 30 deletions httpie/cli/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
$ http example.org hello=world # => POST
""",
).completer = ChoicesCompleter(
('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
completer=ChoicesCompleter(('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
)
positional_arguments.add_argument(
dest='url',
metavar='URL',
Expand All @@ -82,7 +82,8 @@
$ http :/foo # => http://localhost/foo
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
positional_arguments.add_argument(
dest='request_items',
metavar='REQUEST_ITEM',
Expand Down Expand Up @@ -139,7 +140,8 @@
field-name-with\:colon=value
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)

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

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

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

#######################################################################
# Authentication
Expand Down Expand Up @@ -675,7 +686,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
(-a username), HTTPie will prompt for the password.
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
authentication.add_argument(
'--auth-type',
'-A',
Expand All @@ -686,7 +698,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 @@ -720,7 +732,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
and $HTTPS_proxy are supported as well.
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
network.add_argument(
'--follow',
'-F',
Expand All @@ -738,16 +751,18 @@ 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(())
completer=ChoicesCompleter(()),
)
network.add_argument(
'--max-headers',
type=int,
default=0,
short_help=(
'The maximum number of response headers to be read before '
'giving up (default 0, i.e., no limit).'
)
).completer = ChoicesCompleter(())
),
completer=ChoicesCompleter(()),
)

network.add_argument(
'--timeout',
Expand All @@ -764,7 +779,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
the underlying socket for timeout seconds).
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
network.add_argument(
'--check-status',
default=False,
Expand Down Expand Up @@ -814,7 +830,8 @@ 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'))
completer=ChoicesCompleter(('yes', 'no')),
)
ssl.add_argument(
'--ssl',
dest='ssl_version',
Expand All @@ -828,7 +845,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
are shown here).
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
ssl.add_argument(
'--ciphers',
short_help='A string in the OpenSSL cipher list format.',
Expand All @@ -840,7 +858,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
{DEFAULT_SSL_CIPHERS}
""",
).completer = ChoicesCompleter(())
completer=ChoicesCompleter(()),
)
ssl.add_argument(
'--cert',
default=None,
Expand All @@ -852,7 +871,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
specify --cert-key separately.
""",
).completer = FilesCompleter(('crt', 'cert', 'pem'))
completer=FilesCompleter(('crt', 'cert', 'pem')),
)
ssl.add_argument(
'--cert-key',
default=None,
Expand All @@ -863,7 +883,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
certificate file does not contain the private key.
""",
).completer = FilesCompleter(('key', 'pem'))
completer=FilesCompleter(('key', 'pem')),
)

ssl.add_argument(
'--cert-key-pass',
Expand All @@ -874,8 +895,9 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
The passphrase to be used to with the given private key. Only needed if --cert-key
is given and the key file requires a passphrase.
If not provided, you’ll be prompted interactively.
"""
).completer = ChoicesCompleter(())
""",
completer=ChoicesCompleter(()),
)

#######################################################################
# Troubleshooting
Expand Down Expand Up @@ -916,8 +938,9 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
troubleshooting.add_argument(
'--default-scheme',
default='http',
short_help='The default scheme to use if not specified in the URL.'
).completer = ChoicesCompleter(('http', 'https'))
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
6 changes: 4 additions & 2 deletions httpie/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def __getattr__(self, attribute_name):
Qualifiers.ZERO_OR_MORE: argparse.ZERO_OR_MORE,
Qualifiers.ONE_OR_MORE: argparse.ONE_OR_MORE
}
ARGPARSE_IGNORE_KEYS = ('short_help', 'nested_options')
ARGPARSE_IGNORE_KEYS = ('short_help', 'nested_options', 'completer')


def to_argparse(
Expand All @@ -211,12 +211,14 @@ def to_argparse(
concrete_group = concrete_group.add_mutually_exclusive_group(required=False)

for abstract_argument in abstract_group.arguments:
concrete_group.add_argument(
argument = concrete_group.add_argument(
*abstract_argument.aliases,
**drop_keys(map_qualifiers(
abstract_argument.configuration, ARGPARSE_QUALIFIER_MAP
), ARGPARSE_IGNORE_KEYS)
)
if 'completer' in abstract_argument.configuration:
argument.completer = abstract_argument.configuration['completer']

return concrete_parser

Expand Down

0 comments on commit 8497692

Please sign in to comment.