Skip to content

Commit 0bb1053

Browse files
committed
Add shell completion support via argcomplete
1 parent 265841f commit 0bb1053

File tree

5 files changed

+46
-27
lines changed

5 files changed

+46
-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 --no-defaults 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-26
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,13 +611,12 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
608611
exchange.
609612
610613
""",
611-
)
614+
).completer = FilesCompleter(('json',))
612615

613616
#######################################################################
614617
# Authentication
615618
#######################################################################
616619

617-
618620
def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
619621
text = """
620622
The authentication mechanism to be used. Defaults to "{default}".
@@ -672,7 +674,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
672674
(-a username), HTTPie will prompt for the password.
673675
674676
""",
675-
)
677+
).completer = ChoicesCompleter(())
676678
authentication.add_argument(
677679
'--auth-type',
678680
'-A',
@@ -683,7 +685,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
683685
cache=False,
684686
short_help='The authentication mechanism to be used.',
685687
help_formatter=format_auth_help,
686-
)
688+
).completer = ChoicesCompleter(())
687689
authentication.add_argument(
688690
'--ignore-netrc',
689691
default=False,
@@ -717,7 +719,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
717719
and $HTTPS_proxy are supported as well.
718720
719721
""",
720-
)
722+
).completer = ChoicesCompleter(())
721723
network.add_argument(
722724
'--follow',
723725
'-F',
@@ -735,7 +737,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
735737
By default, requests have a limit of 30 redirects (works with --follow).
736738
737739
""",
738-
)
740+
).completer = ChoicesCompleter(())
739741
network.add_argument(
740742
'--max-headers',
741743
type=int,
@@ -744,7 +746,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
744746
'The maximum number of response headers to be read before '
745747
'giving up (default 0, i.e., no limit).'
746748
)
747-
)
749+
).completer = ChoicesCompleter(())
748750

749751
network.add_argument(
750752
'--timeout',
@@ -761,7 +763,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
761763
the underlying socket for timeout seconds).
762764
763765
""",
764-
)
766+
).completer = ChoicesCompleter(())
765767
network.add_argument(
766768
'--check-status',
767769
default=False,
@@ -811,7 +813,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
811813
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
812814
variable instead.)
813815
""",
814-
)
816+
).completer = ChoicesCompleter(('yes', 'no'))
815817
ssl.add_argument(
816818
'--ssl',
817819
dest='ssl_version',
@@ -825,7 +827,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
825827
are shown here).
826828
827829
""",
828-
)
830+
).completer = ChoicesCompleter(())
829831
ssl.add_argument(
830832
'--ciphers',
831833
short_help='A string in the OpenSSL cipher list format.',
@@ -837,7 +839,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
837839
{DEFAULT_SSL_CIPHERS}
838840
839841
""",
840-
)
842+
).completer = ChoicesCompleter(())
841843
ssl.add_argument(
842844
'--cert',
843845
default=None,
@@ -849,7 +851,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
849851
specify --cert-key separately.
850852
851853
""",
852-
)
854+
).completer = FilesCompleter(('crt', 'cert', 'pem'))
853855
ssl.add_argument(
854856
'--cert-key',
855857
default=None,
@@ -860,7 +862,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
860862
certificate file does not contain the private key.
861863
862864
""",
863-
)
865+
).completer = FilesCompleter(('key', 'pem'))
864866

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

877879
#######################################################################
878880
# Troubleshooting
@@ -914,7 +916,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
914916
'--default-scheme',
915917
default='http',
916918
short_help='The default scheme to use if not specified in the URL.'
917-
)
919+
).completer = ChoicesCompleter(('http', 'https'))
918920
troubleshooting.add_argument(
919921
'--debug',
920922
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

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
'setuptools',
4242
'importlib-metadata>=1.4.0; python_version < "3.8"',
4343
'rich>=9.10.0'
44+
'argcomplete',
4445
]
4546
install_requires_win_only = [
4647
'colorama>=0.2.4',

0 commit comments

Comments
 (0)