Skip to content

Commit 93161de

Browse files
committed
Add shell completion support via argcomplete
1 parent 28d11b2 commit 93161de

File tree

2 files changed

+58
-32
lines changed

2 files changed

+58
-32
lines changed

httpie/cli/definition.py

+54-30
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
$ http example.org hello=world # => POST
6767
6868
""",
69-
).completer = ChoicesCompleter(
70-
('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
69+
completer=ChoicesCompleter(('GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'))
70+
)
7171
positional_arguments.add_argument(
7272
dest='url',
7373
metavar='URL',
@@ -82,7 +82,8 @@
8282
$ http :/foo # => http://localhost/foo
8383
8484
""",
85-
).completer = ChoicesCompleter(())
85+
completer=ChoicesCompleter(()),
86+
)
8687
positional_arguments.add_argument(
8788
dest='request_items',
8889
metavar='REQUEST_ITEM',
@@ -139,7 +140,8 @@
139140
field-name-with\:colon=value
140141
141142
""",
142-
).completer = ChoicesCompleter(())
143+
completer=ChoicesCompleter(()),
144+
)
143145

144146
#######################################################################
145147
# Content type.
@@ -192,8 +194,9 @@
192194
short_help=(
193195
'Specify a custom boundary string for multipart/form-data requests. '
194196
'Only has effect only together with --form.'
195-
)
196-
).completer = ChoicesCompleter(())
197+
),
198+
completer=ChoicesCompleter(()),
199+
)
197200
content_types.add_argument(
198201
'--raw',
199202
short_help='Pass raw request data without extra processing.',
@@ -354,7 +357,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
354357
--response-charset=big5
355358
356359
""",
357-
).completer = ChoicesCompleter(())
360+
completer=ChoicesCompleter(()),
361+
)
358362
output_processing.add_argument(
359363
'--response-mime',
360364
metavar='MIME_TYPE',
@@ -367,7 +371,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
367371
--response-mime=text/xml
368372
369373
""",
370-
).completer = ChoicesCompleter(())
374+
completer=ChoicesCompleter(()),
375+
)
371376
output_processing.add_argument(
372377
'--format-options',
373378
action='append',
@@ -392,7 +397,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
392397
f' {option}' for option in DEFAULT_FORMAT_OPTIONS
393398
).strip()
394399
),
395-
).completer = ChoicesCompleter(())
400+
completer=ChoicesCompleter(()),
401+
)
396402

397403
#######################################################################
398404
# Output options
@@ -421,7 +427,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
421427
response body is printed by default.
422428
423429
""",
424-
).completer = ChoicesCompleter(())
430+
completer=ChoicesCompleter(()),
431+
)
425432
output_options.add_argument(
426433
'--headers',
427434
'-h',
@@ -495,7 +502,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
495502
dest='output_options_history',
496503
metavar='WHAT',
497504
help=Qualifiers.SUPPRESS,
498-
).completer = ChoicesCompleter(())
505+
completer=ChoicesCompleter(()),
506+
)
499507
output_options.add_argument(
500508
'--stream',
501509
'-S',
@@ -529,7 +537,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
529537
printed to stderr.
530538
531539
""",
532-
).completer = FilesCompleter()
540+
completer=FilesCompleter(),
541+
)
533542

534543
output_options.add_argument(
535544
'--download',
@@ -600,7 +609,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
600609
601610
https://httpie.io/docs/cli/config-file-directory
602611
""",
603-
).completer = FilesCompleter(('json',))
612+
completer=FilesCompleter(('json',)),
613+
)
604614
sessions.add_argument(
605615
'--session-read-only',
606616
metavar='SESSION_NAME_OR_PATH',
@@ -611,7 +621,8 @@ def format_style_help(available_styles, *, isolation_mode: bool = False):
611621
exchange.
612622
613623
""",
614-
).completer = FilesCompleter(('json',))
624+
completer=FilesCompleter(('json',)),
625+
)
615626

616627
#######################################################################
617628
# Authentication
@@ -675,7 +686,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
675686
(-a username), HTTPie will prompt for the password.
676687
677688
""",
678-
).completer = ChoicesCompleter(())
689+
completer=ChoicesCompleter(()),
690+
)
679691
authentication.add_argument(
680692
'--auth-type',
681693
'-A',
@@ -686,7 +698,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
686698
cache=False,
687699
short_help='The authentication mechanism to be used.',
688700
help_formatter=format_auth_help,
689-
).completer = ChoicesCompleter(())
701+
completer=ChoicesCompleter(()),
702+
)
690703
authentication.add_argument(
691704
'--ignore-netrc',
692705
default=False,
@@ -720,7 +733,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
720733
and $HTTPS_proxy are supported as well.
721734
722735
""",
723-
).completer = ChoicesCompleter(())
736+
completer=ChoicesCompleter(()),
737+
)
724738
network.add_argument(
725739
'--follow',
726740
'-F',
@@ -738,16 +752,18 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
738752
By default, requests have a limit of 30 redirects (works with --follow).
739753
740754
""",
741-
).completer = ChoicesCompleter(())
755+
completer=ChoicesCompleter(()),
756+
)
742757
network.add_argument(
743758
'--max-headers',
744759
type=int,
745760
default=0,
746761
short_help=(
747762
'The maximum number of response headers to be read before '
748763
'giving up (default 0, i.e., no limit).'
749-
)
750-
).completer = ChoicesCompleter(())
764+
),
765+
completer=ChoicesCompleter(()),
766+
)
751767

752768
network.add_argument(
753769
'--timeout',
@@ -764,7 +780,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
764780
the underlying socket for timeout seconds).
765781
766782
""",
767-
).completer = ChoicesCompleter(())
783+
completer=ChoicesCompleter(()),
784+
)
768785
network.add_argument(
769786
'--check-status',
770787
default=False,
@@ -814,7 +831,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
814831
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
815832
variable instead.)
816833
""",
817-
).completer = ChoicesCompleter(('yes', 'no'))
834+
completer=ChoicesCompleter(('yes', 'no')),
835+
)
818836
ssl.add_argument(
819837
'--ssl',
820838
dest='ssl_version',
@@ -828,7 +846,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
828846
are shown here).
829847
830848
""",
831-
).completer = ChoicesCompleter(())
849+
completer=ChoicesCompleter(()),
850+
)
832851
ssl.add_argument(
833852
'--ciphers',
834853
short_help='A string in the OpenSSL cipher list format.',
@@ -840,7 +859,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
840859
{DEFAULT_SSL_CIPHERS}
841860
842861
""",
843-
).completer = ChoicesCompleter(())
862+
completer=ChoicesCompleter(()),
863+
)
844864
ssl.add_argument(
845865
'--cert',
846866
default=None,
@@ -852,7 +872,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
852872
specify --cert-key separately.
853873
854874
""",
855-
).completer = FilesCompleter(('crt', 'cert', 'pem'))
875+
completer=FilesCompleter(('crt', 'cert', 'pem')),
876+
)
856877
ssl.add_argument(
857878
'--cert-key',
858879
default=None,
@@ -863,7 +884,8 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
863884
certificate file does not contain the private key.
864885
865886
""",
866-
).completer = FilesCompleter(('key', 'pem'))
887+
completer=FilesCompleter(('key', 'pem')),
888+
)
867889

868890
ssl.add_argument(
869891
'--cert-key-pass',
@@ -874,8 +896,9 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
874896
The passphrase to be used to with the given private key. Only needed if --cert-key
875897
is given and the key file requires a passphrase.
876898
If not provided, you’ll be prompted interactively.
877-
"""
878-
).completer = ChoicesCompleter(())
899+
""",
900+
completer=ChoicesCompleter(()),
901+
)
879902

880903
#######################################################################
881904
# Troubleshooting
@@ -916,8 +939,9 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
916939
troubleshooting.add_argument(
917940
'--default-scheme',
918941
default='http',
919-
short_help='The default scheme to use if not specified in the URL.'
920-
).completer = ChoicesCompleter(('http', 'https'))
942+
short_help='The default scheme to use if not specified in the URL.',
943+
completer=ChoicesCompleter(('http', 'https')),
944+
)
921945
troubleshooting.add_argument(
922946
'--debug',
923947
action='store_true',

httpie/cli/options.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def __getattr__(self, attribute_name):
187187
Qualifiers.ZERO_OR_MORE: argparse.ZERO_OR_MORE,
188188
Qualifiers.ONE_OR_MORE: argparse.ONE_OR_MORE
189189
}
190-
ARGPARSE_IGNORE_KEYS = ('short_help', 'nested_options')
190+
ARGPARSE_IGNORE_KEYS = ('short_help', 'nested_options', 'completer')
191191

192192

193193
def to_argparse(
@@ -211,12 +211,14 @@ def to_argparse(
211211
concrete_group = concrete_group.add_mutually_exclusive_group(required=False)
212212

213213
for abstract_argument in abstract_group.arguments:
214-
concrete_group.add_argument(
214+
argument = concrete_group.add_argument(
215215
*abstract_argument.aliases,
216216
**drop_keys(map_qualifiers(
217217
abstract_argument.configuration, ARGPARSE_QUALIFIER_MAP
218218
), ARGPARSE_IGNORE_KEYS)
219219
)
220+
if 'completer' in abstract_argument.configuration:
221+
argument.completer = abstract_argument.configuration['completer']
220222

221223
return concrete_parser
222224

0 commit comments

Comments
 (0)