Skip to content

Commit 28d11b2

Browse files
committed
Add shell completion support via argcomplete
1 parent 47e9b99 commit 28d11b2

File tree

5 files changed

+47
-27
lines changed

5 files changed

+47
-27
lines changed

docs/README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -2537,6 +2537,17 @@ The two modes, `--pretty=all` (default for terminal) and `--pretty=none` (defaul
25372537
In the future, the command line syntax and some of the `--OPTIONS` may change slightly, as HTTPie improves and new features are added.
25382538
All changes are recorded in the [change log](#change-log).
25392539
2540+
### Shell completion
2541+
2542+
Shell completion is provided using the argcomplete library. It is suggested
2543+
to load the completion without falling back to the shell defaults in order
2544+
to avoid default completions in contexts where they do not apply. For example
2545+
for bash:
2546+
2547+
```bash
2548+
$ eval "$(register-python-argcomplete --complete-arguments -- http https)"
2549+
```
2550+
25402551
### Community and Support
25412552
25422553
HTTPie has the following community channels:
@@ -2549,10 +2560,11 @@ HTTPie has the following community channels:
25492560
25502561
#### Dependencies
25512562
2552-
Under the hood, HTTPie uses these two amazing libraries:
2563+
Under the hood, HTTPie uses these three amazing libraries:
25532564
25542565
- [Requests](https://requests.readthedocs.io/en/latest/) — Python HTTP library for humans
25552566
- [Pygments](https://pygments.org/) — Python syntax highlighter
2567+
- [argcomplete](https://github.com/kislyuk/argcomplete) — Shell completion generator
25562568
25572569
#### HTTPie friends
25582570

httpie/__main__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# PYTHON_ARGCOMPLETE_OK
12
"""The main entry point. Invoke as `http' or `python -m httpie'.
23
34
"""

httpie/cli/definition.py

+28-25
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import textwrap
44
from argparse import FileType
55

6+
from argcomplete.completers import ChoicesCompleter, FilesCompleter
7+
68
from httpie import __doc__, __version__
79
from httpie.cli.argtypes import (KeyValueArgType, SessionNameValidator,
810
SSLCredentials, readable_file_arg,
@@ -64,7 +66,8 @@
6466
$ http example.org hello=world # => POST
6567
6668
""",
67-
)
69+
).completer = ChoicesCompleter(
70+
('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
6871
positional_arguments.add_argument(
6972
dest='url',
7073
metavar='URL',
@@ -79,7 +82,7 @@
7982
$ http :/foo # => http://localhost/foo
8083
8184
""",
82-
)
85+
).completer = ChoicesCompleter(())
8386
positional_arguments.add_argument(
8487
dest='request_items',
8588
metavar='REQUEST_ITEM',
@@ -136,7 +139,7 @@
136139
field-name-with\:colon=value
137140
138141
""",
139-
)
142+
).completer = ChoicesCompleter(())
140143

141144
#######################################################################
142145
# Content type.
@@ -190,7 +193,7 @@
190193
'Specify a custom boundary string for multipart/form-data requests. '
191194
'Only has effect only together with --form.'
192195
)
193-
)
196+
).completer = ChoicesCompleter(())
194197
content_types.add_argument(
195198
'--raw',
196199
short_help='Pass raw request data without extra processing.',
@@ -351,7 +354,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
351354
--response-charset=big5
352355
353356
""",
354-
)
357+
).completer = ChoicesCompleter(())
355358
output_processing.add_argument(
356359
'--response-mime',
357360
metavar='MIME_TYPE',
@@ -364,7 +367,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
364367
--response-mime=text/xml
365368
366369
""",
367-
)
370+
).completer = ChoicesCompleter(())
368371
output_processing.add_argument(
369372
'--format-options',
370373
action='append',
@@ -389,7 +392,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
389392
f' {option}' for option in DEFAULT_FORMAT_OPTIONS
390393
).strip()
391394
),
392-
)
395+
).completer = ChoicesCompleter(())
393396

394397
#######################################################################
395398
# Output options
@@ -418,7 +421,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
418421
response body is printed by default.
419422
420423
""",
421-
)
424+
).completer = ChoicesCompleter(())
422425
output_options.add_argument(
423426
'--headers',
424427
'-h',
@@ -492,7 +495,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
492495
dest='output_options_history',
493496
metavar='WHAT',
494497
help=Qualifiers.SUPPRESS,
495-
)
498+
).completer = ChoicesCompleter(())
496499
output_options.add_argument(
497500
'--stream',
498501
'-S',
@@ -526,7 +529,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
526529
printed to stderr.
527530
528531
""",
529-
)
532+
).completer = FilesCompleter()
530533

531534
output_options.add_argument(
532535
'--download',
@@ -597,7 +600,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
597600
598601
https://httpie.io/docs/cli/config-file-directory
599602
""",
600-
)
603+
).completer = FilesCompleter(('json',))
601604
sessions.add_argument(
602605
'--session-read-only',
603606
metavar='SESSION_NAME_OR_PATH',
@@ -608,7 +611,7 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
608611
exchange.
609612
610613
""",
611-
)
614+
).completer = FilesCompleter(('json',))
612615

613616
#######################################################################
614617
# Authentication
@@ -672,7 +675,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
672675
(-a username), HTTPie will prompt for the password.
673676
674677
""",
675-
)
678+
).completer = ChoicesCompleter(())
676679
authentication.add_argument(
677680
'--auth-type',
678681
'-A',
@@ -683,7 +686,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
683686
cache=False,
684687
short_help='The authentication mechanism to be used.',
685688
help_formatter=format_auth_help,
686-
)
689+
).completer = ChoicesCompleter(())
687690
authentication.add_argument(
688691
'--ignore-netrc',
689692
default=False,
@@ -717,7 +720,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
717720
and $HTTPS_proxy are supported as well.
718721
719722
""",
720-
)
723+
).completer = ChoicesCompleter(())
721724
network.add_argument(
722725
'--follow',
723726
'-F',
@@ -735,7 +738,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
735738
By default, requests have a limit of 30 redirects (works with --follow).
736739
737740
""",
738-
)
741+
).completer = ChoicesCompleter(())
739742
network.add_argument(
740743
'--max-headers',
741744
type=int,
@@ -744,7 +747,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
744747
'The maximum number of response headers to be read before '
745748
'giving up (default 0, i.e., no limit).'
746749
)
747-
)
750+
).completer = ChoicesCompleter(())
748751

749752
network.add_argument(
750753
'--timeout',
@@ -761,7 +764,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
761764
the underlying socket for timeout seconds).
762765
763766
""",
764-
)
767+
).completer = ChoicesCompleter(())
765768
network.add_argument(
766769
'--check-status',
767770
default=False,
@@ -811,7 +814,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
811814
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
812815
variable instead.)
813816
""",
814-
)
817+
).completer = ChoicesCompleter(('yes', 'no'))
815818
ssl.add_argument(
816819
'--ssl',
817820
dest='ssl_version',
@@ -825,7 +828,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
825828
are shown here).
826829
827830
""",
828-
)
831+
).completer = ChoicesCompleter(())
829832
ssl.add_argument(
830833
'--ciphers',
831834
short_help='A string in the OpenSSL cipher list format.',
@@ -837,7 +840,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
837840
{DEFAULT_SSL_CIPHERS}
838841
839842
""",
840-
)
843+
).completer = ChoicesCompleter(())
841844
ssl.add_argument(
842845
'--cert',
843846
default=None,
@@ -849,7 +852,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
849852
specify --cert-key separately.
850853
851854
""",
852-
)
855+
).completer = FilesCompleter(('crt', 'cert', 'pem'))
853856
ssl.add_argument(
854857
'--cert-key',
855858
default=None,
@@ -860,7 +863,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
860863
certificate file does not contain the private key.
861864
862865
""",
863-
)
866+
).completer = FilesCompleter(('key', 'pem'))
864867

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

877880
#######################################################################
878881
# Troubleshooting
@@ -914,7 +917,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
914917
'--default-scheme',
915918
default='http',
916919
short_help='The default scheme to use if not specified in the URL.'
917-
)
920+
).completer = ChoicesCompleter(('http', 'https'))
918921
troubleshooting.add_argument(
919922
'--debug',
920923
action='store_true',

httpie/core.py

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import socket
66
from typing import List, Optional, Union, Callable
77

8+
import argcomplete
89
import requests
910
from pygments import __version__ as pygments_version
1011
from requests import __version__ as requests_version
@@ -73,6 +74,8 @@ def handle_generic_error(e, annotation=None):
7374

7475
exit_status = ExitStatus.SUCCESS
7576

77+
argcomplete.autocomplete(parser)
78+
7679
try:
7780
parsed_args = parser.parse_args(
7881
args=args,

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
'multidict>=4.7.0',
4141
'setuptools',
4242
'importlib-metadata>=1.4.0; python_version < "3.8"',
43-
'rich>=9.10.0'
43+
'rich>=9.10.0',
44+
'argcomplete'
4445
]
4546
install_requires_win_only = [
4647
'colorama>=0.2.4',

0 commit comments

Comments
 (0)