mirror of
https://github.com/httpie/cli.git
synced 2024-11-28 18:53:21 +01:00
Added docstrings, refactored input.
This commit is contained in:
parent
f26f2f1438
commit
f45cc0eec0
@ -76,7 +76,7 @@ Raw JSON fields (``field:=value``)
|
|||||||
This item type is needed when ``Content-Type`` is JSON and a field's value
|
This item type is needed when ``Content-Type`` is JSON and a field's value
|
||||||
is a ``Boolean``, ``Number``, nested ``Object`` or an ``Array``, because
|
is a ``Boolean``, ``Number``, nested ``Object`` or an ``Array``, because
|
||||||
simple data items are always serialized as ``String``.
|
simple data items are always serialized as ``String``.
|
||||||
E.g. ``pies:=[1,2,3]``,or ``'meals=["ham", "spam"]'`` (mind the quotes).
|
E.g. ``pies:=[1,2,3]``, or ``'meals=["ham", "spam"]'`` (mind the quotes).
|
||||||
|
|
||||||
File fields (``field@/path/to/file``)
|
File fields (``field@/path/to/file``)
|
||||||
Only available with ``-f`` / ``--form``. Use ``@`` as the separator, e.g.,
|
Only available with ``-f`` / ``--form``. Use ``@`` as the separator, e.g.,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
"""
|
"""The main entry point. Invoke as `http' or `python -m httpie'.
|
||||||
The main entry point. Invoke as `http' or `python -m httpie'.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
"""
|
"""CLI arguments definition.
|
||||||
CLI definition.
|
|
||||||
|
NOTE: the CLI interface may change before reaching v1.0.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from . import __doc__
|
from . import __doc__
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from . import cliparse
|
|
||||||
from .output import AVAILABLE_STYLES
|
from .output import AVAILABLE_STYLES
|
||||||
|
from .input import (Parser, AuthCredentialsArgType, KeyValueArgType,
|
||||||
|
PRETTIFY_STDOUT_TTY_ONLY,
|
||||||
|
SEP_PROXY, SEP_CREDENTIALS, SEP_GROUP_ITEMS,
|
||||||
|
OUT_REQ_HEAD, OUT_REQ_BODY, OUT_RESP_HEAD,
|
||||||
|
OUT_RESP_BODY, OUTPUT_OPTIONS)
|
||||||
|
|
||||||
|
|
||||||
def _(text):
|
def _(text):
|
||||||
"""Normalize white space."""
|
"""Normalize whitespace."""
|
||||||
return ' '.join(text.strip().split())
|
return ' '.join(text.strip().split())
|
||||||
|
|
||||||
|
|
||||||
desc = '%s <http://httpie.org>'
|
parser = Parser(description='%s <http://httpie.org>' % __doc__.strip())
|
||||||
parser = cliparse.Parser(
|
|
||||||
description=desc % __doc__.strip(),
|
|
||||||
)
|
|
||||||
parser.add_argument('--version', action='version', version=__version__)
|
parser.add_argument('--version', action='version', version=__version__)
|
||||||
|
|
||||||
|
|
||||||
@ -58,7 +60,7 @@ parser.add_argument(
|
|||||||
prettify = parser.add_mutually_exclusive_group(required=False)
|
prettify = parser.add_mutually_exclusive_group(required=False)
|
||||||
prettify.add_argument(
|
prettify.add_argument(
|
||||||
'--pretty', dest='prettify', action='store_true',
|
'--pretty', dest='prettify', action='store_true',
|
||||||
default=cliparse.PRETTIFY_STDOUT_TTY_ONLY,
|
default=PRETTIFY_STDOUT_TTY_ONLY,
|
||||||
help=_('''
|
help=_('''
|
||||||
If stdout is a terminal, the response is prettified
|
If stdout is a terminal, the response is prettified
|
||||||
by default (colorized and indented if it is JSON).
|
by default (colorized and indented if it is JSON).
|
||||||
@ -85,35 +87,35 @@ output_options.add_argument('--print', '-p', dest='output_options',
|
|||||||
If the output is piped to another program or to a file,
|
If the output is piped to another program or to a file,
|
||||||
then only the body is printed by default.
|
then only the body is printed by default.
|
||||||
'''.format(
|
'''.format(
|
||||||
request_headers=cliparse.OUT_REQ_HEAD,
|
request_headers=OUT_REQ_HEAD,
|
||||||
request_body=cliparse.OUT_REQ_BODY,
|
request_body=OUT_REQ_BODY,
|
||||||
response_headers=cliparse.OUT_RESP_HEAD,
|
response_headers=OUT_RESP_HEAD,
|
||||||
response_body=cliparse.OUT_RESP_BODY,
|
response_body=OUT_RESP_BODY,
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
output_options.add_argument(
|
output_options.add_argument(
|
||||||
'--verbose', '-v', dest='output_options',
|
'--verbose', '-v', dest='output_options',
|
||||||
action='store_const', const=''.join(cliparse.OUTPUT_OPTIONS),
|
action='store_const', const=''.join(OUTPUT_OPTIONS),
|
||||||
help=_('''
|
help=_('''
|
||||||
Print the whole request as well as the response.
|
Print the whole request as well as the response.
|
||||||
Shortcut for --print={0}.
|
Shortcut for --print={0}.
|
||||||
'''.format(''.join(cliparse.OUTPUT_OPTIONS)))
|
'''.format(''.join(OUTPUT_OPTIONS)))
|
||||||
)
|
)
|
||||||
output_options.add_argument(
|
output_options.add_argument(
|
||||||
'--headers', '-h', dest='output_options',
|
'--headers', '-h', dest='output_options',
|
||||||
action='store_const', const=cliparse.OUT_RESP_HEAD,
|
action='store_const', const=OUT_RESP_HEAD,
|
||||||
help=_('''
|
help=_('''
|
||||||
Print only the response headers.
|
Print only the response headers.
|
||||||
Shortcut for --print={0}.
|
Shortcut for --print={0}.
|
||||||
'''.format(cliparse.OUT_RESP_HEAD))
|
'''.format(OUT_RESP_HEAD))
|
||||||
)
|
)
|
||||||
output_options.add_argument(
|
output_options.add_argument(
|
||||||
'--body', '-b', dest='output_options',
|
'--body', '-b', dest='output_options',
|
||||||
action='store_const', const=cliparse.OUT_RESP_BODY,
|
action='store_const', const=OUT_RESP_BODY,
|
||||||
help=_('''
|
help=_('''
|
||||||
Print only the response body.
|
Print only the response body.
|
||||||
Shortcut for --print={0}.
|
Shortcut for --print={0}.
|
||||||
'''.format(cliparse.OUT_RESP_BODY))
|
'''.format(OUT_RESP_BODY))
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -149,7 +151,7 @@ parser.add_argument(
|
|||||||
# ``requests.request`` keyword arguments.
|
# ``requests.request`` keyword arguments.
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--auth', '-a',
|
'--auth', '-a',
|
||||||
type=cliparse.AuthCredentialsArgType(cliparse.SEP_CREDENTIALS),
|
type=AuthCredentialsArgType(SEP_CREDENTIALS),
|
||||||
help=_('''
|
help=_('''
|
||||||
username:password.
|
username:password.
|
||||||
If only the username is provided (-a username),
|
If only the username is provided (-a username),
|
||||||
@ -177,7 +179,7 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--proxy', default=[], action='append',
|
'--proxy', default=[], action='append',
|
||||||
type=cliparse.KeyValueArgType(cliparse.SEP_PROXY),
|
type=KeyValueArgType(SEP_PROXY),
|
||||||
help=_('''
|
help=_('''
|
||||||
String mapping protocol to the URL of the proxy
|
String mapping protocol to the URL of the proxy
|
||||||
(e.g. http:foo.bar:3128).
|
(e.g. http:foo.bar:3128).
|
||||||
@ -224,7 +226,7 @@ parser.add_argument(
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'items', nargs='*',
|
'items', nargs='*',
|
||||||
metavar='ITEM',
|
metavar='ITEM',
|
||||||
type=cliparse.KeyValueArgType(*cliparse.SEP_GROUP_ITEMS),
|
type=KeyValueArgType(*SEP_GROUP_ITEMS),
|
||||||
help=_('''
|
help=_('''
|
||||||
A key-value pair whose type is defined by the
|
A key-value pair whose type is defined by the
|
||||||
separator used. It can be an HTTP header (header:value),
|
separator used. It can be an HTTP header (header:value),
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
"""This module provides the main functionality of HTTPie.
|
||||||
|
|
||||||
|
Invocation flow:
|
||||||
|
|
||||||
|
1. Read, validate and process the input (args, `stdin`).
|
||||||
|
2. Create a request and send it, get the response.
|
||||||
|
3. Process and format the requested parts of the request-response exchange.
|
||||||
|
4. Write to `stdout` and exit.
|
||||||
|
|
||||||
|
"""
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -7,8 +17,10 @@ from requests.compat import str
|
|||||||
|
|
||||||
from .models import HTTPMessage, Environment
|
from .models import HTTPMessage, Environment
|
||||||
from .output import OutputProcessor
|
from .output import OutputProcessor
|
||||||
from . import cliparse
|
from .input import (PRETTIFY_STDOUT_TTY_ONLY,
|
||||||
from . import cli
|
OUT_REQ_BODY, OUT_REQ_HEAD,
|
||||||
|
OUT_RESP_HEAD, OUT_RESP_BODY)
|
||||||
|
from .cli import parser
|
||||||
|
|
||||||
|
|
||||||
TYPE_FORM = 'application/x-www-form-urlencoded; charset=utf-8'
|
TYPE_FORM = 'application/x-www-form-urlencoded; charset=utf-8'
|
||||||
@ -16,6 +28,7 @@ TYPE_JSON = 'application/json; charset=utf-8'
|
|||||||
|
|
||||||
|
|
||||||
def get_response(args, env):
|
def get_response(args, env):
|
||||||
|
"""Send the request and return a `request.Response`."""
|
||||||
|
|
||||||
auto_json = args.data and not args.form
|
auto_json = args.data and not args.form
|
||||||
if args.json or auto_json:
|
if args.json or auto_json:
|
||||||
@ -69,23 +82,20 @@ def get_response(args, env):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def get_output(args, env, response):
|
def get_output(args, env, request, response):
|
||||||
|
"""Format parts of the `request`-`response` exchange
|
||||||
|
according to `args` and `env` and return a `unicode`.
|
||||||
|
|
||||||
do_prettify = (
|
"""
|
||||||
args.prettify is True or
|
do_prettify = (args.prettify is True
|
||||||
(args.prettify == cliparse.PRETTIFY_STDOUT_TTY_ONLY
|
or (args.prettify == PRETTIFY_STDOUT_TTY_ONLY
|
||||||
and env.stdout_isatty)
|
and env.stdout_isatty))
|
||||||
)
|
|
||||||
|
|
||||||
do_output_request = (
|
do_output_request = (OUT_REQ_HEAD in args.output_options
|
||||||
cliparse.OUT_REQ_HEAD in args.output_options
|
or OUT_REQ_BODY in args.output_options)
|
||||||
or cliparse.OUT_REQ_BODY in args.output_options
|
|
||||||
)
|
|
||||||
|
|
||||||
do_output_response = (
|
do_output_response = (OUT_RESP_HEAD in args.output_options
|
||||||
cliparse.OUT_RESP_HEAD in args.output_options
|
or OUT_RESP_BODY in args.output_options)
|
||||||
or cliparse.OUT_RESP_BODY in args.output_options
|
|
||||||
)
|
|
||||||
|
|
||||||
prettifier = None
|
prettifier = None
|
||||||
if do_prettify:
|
if do_prettify:
|
||||||
@ -95,10 +105,11 @@ def get_output(args, env, response):
|
|||||||
buf = []
|
buf = []
|
||||||
|
|
||||||
if do_output_request:
|
if do_output_request:
|
||||||
req = HTTPMessage.from_request(response.request).format(
|
req_msg = HTTPMessage.from_request(request)
|
||||||
|
req = req_msg.format(
|
||||||
prettifier=prettifier,
|
prettifier=prettifier,
|
||||||
with_headers=cliparse.OUT_REQ_HEAD in args.output_options,
|
with_headers=OUT_REQ_HEAD in args.output_options,
|
||||||
with_body=cliparse.OUT_REQ_BODY in args.output_options
|
with_body=OUT_REQ_BODY in args.output_options
|
||||||
)
|
)
|
||||||
buf.append(req)
|
buf.append(req)
|
||||||
buf.append('\n')
|
buf.append('\n')
|
||||||
@ -106,10 +117,11 @@ def get_output(args, env, response):
|
|||||||
buf.append('\n')
|
buf.append('\n')
|
||||||
|
|
||||||
if do_output_response:
|
if do_output_response:
|
||||||
resp = HTTPMessage.from_response(response).format(
|
resp_msg = HTTPMessage.from_response(response)
|
||||||
|
resp = resp_msg.format(
|
||||||
prettifier=prettifier,
|
prettifier=prettifier,
|
||||||
with_headers=cliparse.OUT_RESP_HEAD in args.output_options,
|
with_headers=OUT_RESP_HEAD in args.output_options,
|
||||||
with_body=cliparse.OUT_RESP_BODY in args.output_options
|
with_body=OUT_RESP_BODY in args.output_options
|
||||||
)
|
)
|
||||||
buf.append(resp)
|
buf.append(resp)
|
||||||
buf.append('\n')
|
buf.append('\n')
|
||||||
@ -118,10 +130,7 @@ def get_output(args, env, response):
|
|||||||
|
|
||||||
|
|
||||||
def get_exist_status(code, allow_redirects=False):
|
def get_exist_status(code, allow_redirects=False):
|
||||||
"""
|
"""Translate HTTP status code to exit status."""
|
||||||
Translate HTTP status code to exit status.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if 300 <= code <= 399 and not allow_redirects:
|
if 300 <= code <= 399 and not allow_redirects:
|
||||||
# Redirect
|
# Redirect
|
||||||
return 3
|
return 3
|
||||||
@ -136,13 +145,12 @@ def get_exist_status(code, allow_redirects=False):
|
|||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv[1:], env=Environment()):
|
def main(args=sys.argv[1:], env=Environment()):
|
||||||
"""
|
"""Run the main program and write the output to ``env.stdout``.
|
||||||
Run the main program and write the output to ``env.stdout``.
|
|
||||||
|
|
||||||
Return exit status.
|
Return exit status.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
args = cli.parser.parse_args(args=args, env=env)
|
args = parser.parse_args(args=args, env=env)
|
||||||
response = get_response(args, env)
|
response = get_response(args, env)
|
||||||
|
|
||||||
status = 0
|
status = 0
|
||||||
@ -155,7 +163,7 @@ def main(args=sys.argv[1:], env=Environment()):
|
|||||||
response.raw.status, response.raw.reason)
|
response.raw.status, response.raw.reason)
|
||||||
env.stderr.write(err.encode('utf8'))
|
env.stderr.write(err.encode('utf8'))
|
||||||
|
|
||||||
output = get_output(args, env, response)
|
output = get_output(args, env, response.request, response)
|
||||||
output_bytes = output.encode('utf8')
|
output_bytes = output.encode('utf8')
|
||||||
f = getattr(env.stdout, 'buffer', env.stdout)
|
f = getattr(env.stdout, 'buffer', env.stdout)
|
||||||
f.write(output_bytes)
|
f.write(output_bytes)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""
|
"""Parsing and processing of CLI input (args, auth credentials, files, stdin).
|
||||||
CLI argument parsing logic.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
@ -73,6 +72,12 @@ DEFAULT_UA = 'HTTPie/%s' % __version__
|
|||||||
|
|
||||||
|
|
||||||
class Parser(argparse.ArgumentParser):
|
class Parser(argparse.ArgumentParser):
|
||||||
|
"""Adds additional logic to `argparse.ArgumentParser`.
|
||||||
|
|
||||||
|
Handles all input (CLI args, file args, stdin), applies defaults,
|
||||||
|
and performs extra validation.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['add_help'] = False
|
kwargs['add_help'] = False
|
||||||
@ -101,14 +106,18 @@ class Parser(argparse.ArgumentParser):
|
|||||||
return args
|
return args
|
||||||
|
|
||||||
def _body_from_file(self, args, f):
|
def _body_from_file(self, args, f):
|
||||||
|
"""Use the content of `f` as the `request.data`.
|
||||||
|
|
||||||
|
There can only be one source of request data.
|
||||||
|
|
||||||
|
"""
|
||||||
if args.data:
|
if args.data:
|
||||||
self.error('Request body (from stdin or a file) and request '
|
self.error('Request body (from stdin or a file) and request '
|
||||||
'data (key=value) cannot be mixed.')
|
'data (key=value) cannot be mixed.')
|
||||||
args.data = f.read()
|
args.data = f.read()
|
||||||
|
|
||||||
def _guess_method(self, args, env):
|
def _guess_method(self, args, env):
|
||||||
"""
|
"""Set `args.method` if not specified to either POST or GET
|
||||||
Set `args.method` if not specified to either POST or GET
|
|
||||||
based on whether the request has data or not.
|
based on whether the request has data or not.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -143,9 +152,8 @@ class Parser(argparse.ArgumentParser):
|
|||||||
args.method = HTTP_POST if has_data else HTTP_GET
|
args.method = HTTP_POST if has_data else HTTP_GET
|
||||||
|
|
||||||
def _parse_items(self, args):
|
def _parse_items(self, args):
|
||||||
"""
|
"""Parse `args.items` into `args.headers`, `args.data`,
|
||||||
Parse `args.items` into `args.headers`,
|
`args.`, and `args.files`.
|
||||||
`args.data`, `args.`, and `args.files`.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
args.headers = CaseInsensitiveDict()
|
args.headers = CaseInsensitiveDict()
|
||||||
@ -191,6 +199,11 @@ class Parser(argparse.ArgumentParser):
|
|||||||
args.headers['Content-Type'] = content_type
|
args.headers['Content-Type'] = content_type
|
||||||
|
|
||||||
def _process_output_options(self, args, env):
|
def _process_output_options(self, args, env):
|
||||||
|
"""Apply defaults to output options or validate the provided ones.
|
||||||
|
|
||||||
|
The default output options are stdout-type-sensitive.
|
||||||
|
|
||||||
|
"""
|
||||||
if not args.output_options:
|
if not args.output_options:
|
||||||
args.output_options = (OUTPUT_OPTIONS_DEFAULT if env.stdout_isatty
|
args.output_options = (OUTPUT_OPTIONS_DEFAULT if env.stdout_isatty
|
||||||
else OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED)
|
else OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED)
|
||||||
@ -218,8 +231,7 @@ class KeyValue(object):
|
|||||||
|
|
||||||
|
|
||||||
class KeyValueArgType(object):
|
class KeyValueArgType(object):
|
||||||
"""
|
"""A key-value pair argument type used with `argparse`.
|
||||||
A key-value pair argument type used with `argparse`.
|
|
||||||
|
|
||||||
Parses a key-value arg and constructs a `KeyValue` instance.
|
Parses a key-value arg and constructs a `KeyValue` instance.
|
||||||
Used for headers, form data, and other key-value pair types.
|
Used for headers, form data, and other key-value pair types.
|
||||||
@ -232,8 +244,7 @@ class KeyValueArgType(object):
|
|||||||
self.separators = separators
|
self.separators = separators
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
"""
|
"""Parse `string` and return `self.key_value_class()` instance.
|
||||||
Parse `string` and return `self.key_value_class()` instance.
|
|
||||||
|
|
||||||
The best of `self.separators` is determined (first found, longest).
|
The best of `self.separators` is determined (first found, longest).
|
||||||
Back slash escaped characters aren't considered as separators
|
Back slash escaped characters aren't considered as separators
|
||||||
@ -243,12 +254,14 @@ class KeyValueArgType(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class Escaped(str):
|
class Escaped(str):
|
||||||
pass
|
"""Represents an escaped character."""
|
||||||
|
|
||||||
def tokenize(s):
|
def tokenize(s):
|
||||||
"""
|
"""Tokenize `s`. There are only two token types - strings
|
||||||
r'foo\=bar\\baz'
|
and escaped characters:
|
||||||
=> ['foo', Escaped('='), 'bar', Escaped('\'), 'baz']
|
|
||||||
|
>>> tokenize(r'foo\=bar\\baz')
|
||||||
|
['foo', Escaped('='), 'bar', Escaped('\\'), 'baz']
|
||||||
|
|
||||||
"""
|
"""
|
||||||
tokens = ['']
|
tokens = ['']
|
||||||
@ -305,10 +318,8 @@ class KeyValueArgType(object):
|
|||||||
|
|
||||||
|
|
||||||
class AuthCredentials(KeyValue):
|
class AuthCredentials(KeyValue):
|
||||||
"""
|
"""Represents parsed credentials."""
|
||||||
Represents parsed credentials.
|
|
||||||
|
|
||||||
"""
|
|
||||||
def _getpass(self, prompt):
|
def _getpass(self, prompt):
|
||||||
# To allow mocking.
|
# To allow mocking.
|
||||||
return getpass.getpass(prompt)
|
return getpass.getpass(prompt)
|
||||||
@ -325,10 +336,16 @@ class AuthCredentials(KeyValue):
|
|||||||
|
|
||||||
|
|
||||||
class AuthCredentialsArgType(KeyValueArgType):
|
class AuthCredentialsArgType(KeyValueArgType):
|
||||||
|
"""A key-value arg type that parses credentials."""
|
||||||
|
|
||||||
key_value_class = AuthCredentials
|
key_value_class = AuthCredentials
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
|
"""Parse credentials from `string`.
|
||||||
|
|
||||||
|
("username" or "username:password").
|
||||||
|
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return super(AuthCredentialsArgType, self).__call__(string)
|
return super(AuthCredentialsArgType, self).__call__(string)
|
||||||
except argparse.ArgumentTypeError:
|
except argparse.ArgumentTypeError:
|
||||||
@ -342,11 +359,11 @@ class AuthCredentialsArgType(KeyValueArgType):
|
|||||||
|
|
||||||
|
|
||||||
class ParamDict(OrderedDict):
|
class ParamDict(OrderedDict):
|
||||||
|
"""Multi-value dict for URL parameters and form data."""
|
||||||
|
|
||||||
#noinspection PyMethodOverriding
|
#noinspection PyMethodOverriding
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
"""
|
""" If `key` is assigned more than once, `self[key]` holds a
|
||||||
If `key` is assigned more than once, `self[key]` holds a
|
|
||||||
`list` of all the values.
|
`list` of all the values.
|
||||||
|
|
||||||
This allows having multiple fields with the same name in form
|
This allows having multiple fields with the same name in form
|
||||||
@ -365,12 +382,10 @@ class ParamDict(OrderedDict):
|
|||||||
|
|
||||||
|
|
||||||
def parse_items(items, data=None, headers=None, files=None, params=None):
|
def parse_items(items, data=None, headers=None, files=None, params=None):
|
||||||
"""
|
"""Parse `KeyValue` `items` into `data`, `headers`, `files`,
|
||||||
Parse `KeyValue` `items` into `data`, `headers`, `files`,
|
|
||||||
and `params`.
|
and `params`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if headers is None:
|
if headers is None:
|
||||||
headers = CaseInsensitiveDict()
|
headers = CaseInsensitiveDict()
|
||||||
if data is None:
|
if data is None:
|
@ -4,6 +4,12 @@ from requests.compat import urlparse, is_windows
|
|||||||
|
|
||||||
|
|
||||||
class Environment(object):
|
class Environment(object):
|
||||||
|
"""Holds information about the execution context.
|
||||||
|
|
||||||
|
Groups various aspects of the environment in a changeable object
|
||||||
|
and allows for mocking.
|
||||||
|
|
||||||
|
"""
|
||||||
stdin_isatty = sys.stdin.isatty()
|
stdin_isatty = sys.stdin.isatty()
|
||||||
stdin = sys.stdin
|
stdin = sys.stdin
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""
|
"""Output processing and formatting.
|
||||||
Colorizing of HTTP messages and content processing.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
@ -22,8 +21,7 @@ AVAILABLE_STYLES = [DEFAULT_STYLE] + list(STYLE_MAP.keys())
|
|||||||
|
|
||||||
|
|
||||||
class HTTPLexer(lexer.RegexLexer):
|
class HTTPLexer(lexer.RegexLexer):
|
||||||
"""
|
"""Simplified HTTP lexer for Pygments.
|
||||||
Simplified HTTP lexer for Pygments.
|
|
||||||
|
|
||||||
It only operates on headers and provides a stronger contrast between
|
It only operates on headers and provides a stronger contrast between
|
||||||
their names and values than the original one bundled with Pygments
|
their names and values than the original one bundled with Pygments
|
||||||
|
@ -37,7 +37,7 @@ from requests.compat import is_py26, is_py3, str
|
|||||||
TESTS_ROOT = os.path.dirname(__file__)
|
TESTS_ROOT = os.path.dirname(__file__)
|
||||||
sys.path.insert(0, os.path.realpath(os.path.join(TESTS_ROOT, '..')))
|
sys.path.insert(0, os.path.realpath(os.path.join(TESTS_ROOT, '..')))
|
||||||
|
|
||||||
from httpie import cliparse
|
from httpie import input
|
||||||
from httpie.models import Environment
|
from httpie.models import Environment
|
||||||
from httpie.core import main, get_output
|
from httpie.core import main, get_output
|
||||||
|
|
||||||
@ -506,7 +506,7 @@ class VerboseFlagTest(BaseTestCase):
|
|||||||
class MultipartFormDataFileUploadTest(BaseTestCase):
|
class MultipartFormDataFileUploadTest(BaseTestCase):
|
||||||
|
|
||||||
def test_non_existent_file_raises_parse_error(self):
|
def test_non_existent_file_raises_parse_error(self):
|
||||||
self.assertRaises(cliparse.ParseError, http,
|
self.assertRaises(input.ParseError, http,
|
||||||
'--form',
|
'--form',
|
||||||
'--traceback',
|
'--traceback',
|
||||||
'POST',
|
'POST',
|
||||||
@ -595,7 +595,7 @@ class AuthTest(BaseTestCase):
|
|||||||
|
|
||||||
def test_password_prompt(self):
|
def test_password_prompt(self):
|
||||||
|
|
||||||
cliparse.AuthCredentials._getpass = lambda self, prompt: 'password'
|
input.AuthCredentials._getpass = lambda self, prompt: 'password'
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--auth',
|
'--auth',
|
||||||
@ -681,12 +681,12 @@ class ExitStatusTest(BaseTestCase):
|
|||||||
class ItemParsingTest(BaseTestCase):
|
class ItemParsingTest(BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.key_value_type = cliparse.KeyValueArgType(
|
self.key_value_type = input.KeyValueArgType(
|
||||||
cliparse.SEP_HEADERS,
|
input.SEP_HEADERS,
|
||||||
cliparse.SEP_QUERY,
|
input.SEP_QUERY,
|
||||||
cliparse.SEP_DATA,
|
input.SEP_DATA,
|
||||||
cliparse.SEP_DATA_RAW_JSON,
|
input.SEP_DATA_RAW_JSON,
|
||||||
cliparse.SEP_FILES,
|
input.SEP_FILES,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_items(self):
|
def test_invalid_items(self):
|
||||||
@ -696,7 +696,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
lambda: self.key_value_type(item))
|
lambda: self.key_value_type(item))
|
||||||
|
|
||||||
def test_escape(self):
|
def test_escape(self):
|
||||||
headers, data, files, params = cliparse.parse_items([
|
headers, data, files, params = input.parse_items([
|
||||||
# headers
|
# headers
|
||||||
self.key_value_type('foo\\:bar:baz'),
|
self.key_value_type('foo\\:bar:baz'),
|
||||||
self.key_value_type('jack\\@jill:hill'),
|
self.key_value_type('jack\\@jill:hill'),
|
||||||
@ -715,7 +715,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
self.assertIn('bar@baz', files)
|
self.assertIn('bar@baz', files)
|
||||||
|
|
||||||
def test_escape_longsep(self):
|
def test_escape_longsep(self):
|
||||||
headers, data, files, params = cliparse.parse_items([
|
headers, data, files, params = input.parse_items([
|
||||||
self.key_value_type('bob\\:==foo'),
|
self.key_value_type('bob\\:==foo'),
|
||||||
])
|
])
|
||||||
self.assertDictEqual(params, {
|
self.assertDictEqual(params, {
|
||||||
@ -723,7 +723,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def test_valid_items(self):
|
def test_valid_items(self):
|
||||||
headers, data, files, params = cliparse.parse_items([
|
headers, data, files, params = input.parse_items([
|
||||||
self.key_value_type('string=value'),
|
self.key_value_type('string=value'),
|
||||||
self.key_value_type('header:value'),
|
self.key_value_type('header:value'),
|
||||||
self.key_value_type('list:=["a", 1, {}, false]'),
|
self.key_value_type('list:=["a", 1, {}, false]'),
|
||||||
@ -754,7 +754,7 @@ class ItemParsingTest(BaseTestCase):
|
|||||||
class ArgumentParserTestCase(unittest.TestCase):
|
class ArgumentParserTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.parser = cliparse.Parser()
|
self.parser = input.Parser()
|
||||||
|
|
||||||
def test_guess_when_method_set_and_valid(self):
|
def test_guess_when_method_set_and_valid(self):
|
||||||
args = argparse.Namespace()
|
args = argparse.Namespace()
|
||||||
@ -795,7 +795,7 @@ class ArgumentParserTestCase(unittest.TestCase):
|
|||||||
self.assertEquals(args.url, 'http://example.com/')
|
self.assertEquals(args.url, 'http://example.com/')
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
args.items,
|
args.items,
|
||||||
[cliparse.KeyValue(
|
[input.KeyValue(
|
||||||
key='data', value='field', sep='=', orig='data=field')])
|
key='data', value='field', sep='=', orig='data=field')])
|
||||||
|
|
||||||
def test_guess_when_method_set_but_invalid_and_header_field(self):
|
def test_guess_when_method_set_but_invalid_and_header_field(self):
|
||||||
@ -813,7 +813,7 @@ class ArgumentParserTestCase(unittest.TestCase):
|
|||||||
self.assertEquals(args.url, 'http://example.com/')
|
self.assertEquals(args.url, 'http://example.com/')
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
args.items,
|
args.items,
|
||||||
[cliparse.KeyValue(
|
[input.KeyValue(
|
||||||
key='test', value='header', sep=':', orig='test:header')])
|
key='test', value='header', sep=':', orig='test:header')])
|
||||||
|
|
||||||
def test_guess_when_method_set_but_invalid_and_item_exists(self):
|
def test_guess_when_method_set_but_invalid_and_item_exists(self):
|
||||||
@ -821,16 +821,16 @@ class ArgumentParserTestCase(unittest.TestCase):
|
|||||||
args.method = 'http://example.com/'
|
args.method = 'http://example.com/'
|
||||||
args.url = 'new_item=a'
|
args.url = 'new_item=a'
|
||||||
args.items = [
|
args.items = [
|
||||||
cliparse.KeyValue(
|
input.KeyValue(
|
||||||
key='old_item', value='b', sep='=', orig='old_item=b')
|
key='old_item', value='b', sep='=', orig='old_item=b')
|
||||||
]
|
]
|
||||||
|
|
||||||
self.parser._guess_method(args, Environment())
|
self.parser._guess_method(args, Environment())
|
||||||
|
|
||||||
self.assertEquals(args.items, [
|
self.assertEquals(args.items, [
|
||||||
cliparse.KeyValue(
|
input.KeyValue(
|
||||||
key='new_item', value='a', sep='=', orig='new_item=a'),
|
key='new_item', value='a', sep='=', orig='new_item=a'),
|
||||||
cliparse.KeyValue(key
|
input.KeyValue(key
|
||||||
='old_item', value='b', sep='=', orig='old_item=b'),
|
='old_item', value='b', sep='=', orig='old_item=b'),
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -879,7 +879,7 @@ class UnicodeOutputTestCase(BaseTestCase):
|
|||||||
args.style = 'default'
|
args.style = 'default'
|
||||||
|
|
||||||
# colorized output contains escape sequences
|
# colorized output contains escape sequences
|
||||||
output = get_output(args, Environment(), response)
|
output = get_output(args, Environment(), response.request, response)
|
||||||
|
|
||||||
for key, value in response_dict.items():
|
for key, value in response_dict.items():
|
||||||
self.assertIn(key, output)
|
self.assertIn(key, output)
|
||||||
|
Loading…
Reference in New Issue
Block a user