diff --git a/httpie/cli/definition.py b/httpie/cli/definition.py index 3f10e3582c..f3ed628709 100644 --- a/httpie/cli/definition.py +++ b/httpie/cli/definition.py @@ -1,4 +1,878 @@ -from httpie.cli.opts_compiler import to_argparse -from httpie.cli.opts import options +from __future__ import annotations +import textwrap +from argparse import FileType + +from httpie import __doc__, __version__ +from httpie.cli.argtypes import (KeyValueArgType, SessionNameValidator, + readable_file_arg, response_charset_type, + response_mime_type) +from httpie.cli.constants import (BASE_OUTPUT_OPTIONS, DEFAULT_FORMAT_OPTIONS, + OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, + OUT_RESP_HEAD, OUT_RESP_META, OUTPUT_OPTIONS, + OUTPUT_OPTIONS_DEFAULT, PRETTY_MAP, + PRETTY_STDOUT_TTY_ONLY, + SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY, + SORTED_FORMAT_OPTIONS_STRING, + UNSORTED_FORMAT_OPTIONS_STRING, RequestType) +from httpie.cli.options import ParserSpec, Qualifiers, to_argparse +from httpie.output.formatters.colors import (AUTO_STYLE, DEFAULT_STYLE, + get_available_styles) +from httpie.plugins.builtin import BuiltinAuthPlugin +from httpie.plugins.registry import plugin_manager +from httpie.sessions import DEFAULT_SESSIONS_DIR +from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS + +options = ParserSpec( + 'http', + description=f'{__doc__.strip()} ', + epilog=""" + For every --OPTION there is also a --no-OPTION that reverts OPTION + to its default value. + + Suggestions and bug reports are greatly appreciated: + + https://github.com/httpie/httpie/issues + + """, +) + +####################################################################### +# Positional arguments. +####################################################################### + +positional_arguments = options.new_group( + 'Positional Arguments', + description=""" + These arguments come after any flags and in the order they are listed here. + Only URL is required. + """, +) + +positional_arguments.add_argument( + dest='method', + metavar='METHOD', + nargs=Qualifiers.OPTIONAL, + default=None, + help=""" + The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...). + + This argument can be omitted in which case HTTPie will use POST if there + is some data to be sent, otherwise GET: + + $ http example.org # => GET + $ http example.org hello=world # => POST + + """, +) +positional_arguments.add_argument( + dest='url', + metavar='URL', + help=""" + The scheme defaults to 'http://' if the URL does not include one. + (You can override this with: --default-scheme=https) + + You can also use a shorthand for localhost + + $ http :3000 # => http://localhost:3000 + $ http :/foo # => http://localhost/foo + + """, +) +positional_arguments.add_argument( + dest='request_items', + metavar='REQUEST_ITEM', + nargs=Qualifiers.ZERO_OR_MORE, + default=None, + type=KeyValueArgType(*SEPARATOR_GROUP_ALL_ITEMS), + help=r""" + Optional key-value pairs to be included in the request. The separator used + determines the type: + + ':' HTTP headers: + + Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0 + + '==' URL parameters to be appended to the request URI: + + search==httpie + + '=' Data fields to be serialized into a JSON object (with --json, -j) + or form data (with --form, -f): + + name=HTTPie language=Python description='CLI HTTP client' + + ':=' Non-string JSON data fields (only with --json, -j): + + awesome:=true amount:=42 colors:='["red", "green", "blue"]' + + '@' Form file fields (only with --form or --multipart): + + cv@~/Documents/CV.pdf + cv@'~/Documents/CV.pdf;type=application/pdf' + + '=@' A data field like '=', but takes a file path and embeds its content: + + essay=@Documents/essay.txt + + ':=@' A raw JSON field like ':=', but takes a file path and embeds its content: + + package:=@./package.json + + You can use a backslash to escape a colliding separator in the field name: + + field-name-with\:colon=value + + """, +) + +####################################################################### +# Content type. +####################################################################### + +content_types = options.new_group('Predefined Content Types') + +content_types.add_argument( + '--json', + '-j', + action='store_const', + const=RequestType.JSON, + dest='request_type', + help=""" + (default) Data items from the command line are serialized as a JSON object. + The Content-Type and Accept headers are set to application/json + (if not specified). + + """, +) +content_types.add_argument( + '--form', + '-f', + action='store_const', + const=RequestType.FORM, + dest='request_type', + help=""" + Data items from the command line are serialized as form fields. + + The Content-Type is set to application/x-www-form-urlencoded (if not + specified). The presence of any file fields results in a + multipart/form-data request. + + """, +) +content_types.add_argument( + '--multipart', + action='store_const', + const=RequestType.MULTIPART, + dest='request_type', + help=""" + Similar to --form, but always sends a multipart/form-data + request (i.e., even without files). + + """, +) +content_types.add_argument( + '--boundary', + help=""" + Specify a custom boundary string for multipart/form-data requests. + Only has effect only together with --form. + + """, +) +content_types.add_argument( + '--raw', + help=""" + This option allows you to pass raw request data without extra processing + (as opposed to the structured request items syntax): + + $ http --raw='data' pie.dev/post + + You can achieve the same by piping the data via stdin: + + $ echo data | http pie.dev/post + + Or have HTTPie load the raw data from a file: + + $ http pie.dev/post @data.txt + + + """, +) + +####################################################################### +# Content processing. +####################################################################### + +processing_options = options.new_group('Content Processing Options') + +processing_options.add_argument( + '--compress', + '-x', + action='count', + default=0, + help=""" + Content compressed (encoded) with Deflate algorithm. + The Content-Encoding header is set to deflate. + + Compression is skipped if it appears that compression ratio is + negative. Compression can be forced by repeating the argument. + + """, +) + +####################################################################### +# Output processing +####################################################################### + + +def format_style_help(available_styles): + return """ + Output coloring style (default is "{default}"). It can be one of: + + {available_styles} + + The "{auto_style}" style follows your terminal's ANSI color styles. + For non-{auto_style} styles to work properly, please make sure that the + $TERM environment variable is set to "xterm-256color" or similar + (e.g., via `export TERM=xterm-256color' in your ~/.bashrc). + """.format( + default=DEFAULT_STYLE, + available_styles='\n'.join( + f' {line.strip()}' + for line in textwrap.wrap(', '.join(available_styles), 60) + ).strip(), + auto_style=AUTO_STYLE, + ) + + +_sorted_kwargs = { + 'action': 'append_const', + 'const': SORTED_FORMAT_OPTIONS_STRING, + 'dest': 'format_options', +} +_unsorted_kwargs = { + 'action': 'append_const', + 'const': UNSORTED_FORMAT_OPTIONS_STRING, + 'dest': 'format_options', +} + +output_processing = options.new_group('Output Processing') + +output_processing.add_argument( + '--pretty', + dest='prettify', + default=PRETTY_STDOUT_TTY_ONLY, + choices=sorted(PRETTY_MAP.keys()), + help=""" + Controls output processing. The value can be "none" to not prettify + the output (default for redirected output), "all" to apply both colors + and formatting (default for terminal output), "colors", or "format". + + """, +) +output_processing.add_argument( + '--style', + '-s', + dest='style', + metavar='STYLE', + default=DEFAULT_STYLE, + action='lazy_choices', + getter=get_available_styles, + help_formatter=format_style_help, +) + +# The closest approx. of the documented resetting to default via --no-