mirror of
https://github.com/httpie/cli.git
synced 2024-11-28 08:38:44 +02:00
Allow multiple fields with the same name.
Applies to form data and URL params: http -f url a=1 a=2 http url a==1 a==2
This commit is contained in:
parent
9944def703
commit
7af08b6faa
@ -328,6 +328,8 @@ Changelog
|
||||
---------
|
||||
|
||||
* `0.2.6dev <https://github.com/jkbr/httpie/compare/0.2.5...master>`_
|
||||
* Form data and URL params can now have mutiple fields with the same name
|
||||
(e.g.,``http -f url a=1 a=2``).
|
||||
* Added ``--check-status`` to exit with an error for HTTP 3xx, 4xx and
|
||||
5xx (3, 4, 5).
|
||||
* If the output is piped to another program or redirected to a file,
|
||||
|
@ -155,7 +155,7 @@ parser.add_argument(
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--auth-type', choices=['basic', 'digest'],
|
||||
'--auth-type', choices=['basic', 'digest'], default='basic',
|
||||
help=_('''
|
||||
The authentication mechanism to be used.
|
||||
Defaults to "basic".
|
||||
|
@ -56,7 +56,6 @@ class Parser(argparse.ArgumentParser):
|
||||
args = super(Parser, self).parse_args(args, namespace)
|
||||
|
||||
self._process_output_options(args, env)
|
||||
self._validate_auth_options(args)
|
||||
self._guess_method(args, env)
|
||||
self._parse_items(args)
|
||||
|
||||
@ -124,9 +123,9 @@ class Parser(argparse.ArgumentParser):
|
||||
"""
|
||||
args.headers = CaseInsensitiveDict()
|
||||
args.headers['User-Agent'] = DEFAULT_UA
|
||||
args.data = OrderedDict()
|
||||
args.data = ParamDict() if args.form else OrderedDict()
|
||||
args.files = OrderedDict()
|
||||
args.params = OrderedDict()
|
||||
args.params = ParamDict()
|
||||
try:
|
||||
parse_items(items=args.items,
|
||||
headers=args.headers,
|
||||
@ -173,10 +172,6 @@ class Parser(argparse.ArgumentParser):
|
||||
','.join(unknown)
|
||||
)
|
||||
|
||||
def _validate_auth_options(self, args):
|
||||
if args.auth_type and not args.auth:
|
||||
self.error('--auth-type can only be used with --auth')
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
pass
|
||||
@ -319,6 +314,28 @@ class AuthCredentialsArgType(KeyValueArgType):
|
||||
)
|
||||
|
||||
|
||||
class ParamDict(OrderedDict):
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
If `key` is assigned more than once, `self[key]` holds a
|
||||
`list` of all the values.
|
||||
|
||||
This allows having multiple fields with the same name in form
|
||||
data and URL params.
|
||||
|
||||
"""
|
||||
# NOTE: Won't work when used for form data with multiple values
|
||||
# for a field and a file field is present:
|
||||
# https://github.com/kennethreitz/requests/issues/737
|
||||
if key not in self:
|
||||
super(ParamDict, self).__setitem__(key, value)
|
||||
else:
|
||||
if not isinstance(self[key], list):
|
||||
super(ParamDict, self).__setitem__(key, [self[key]])
|
||||
self[key].append(value)
|
||||
|
||||
|
||||
def parse_items(items, data=None, headers=None, files=None, params=None):
|
||||
"""
|
||||
Parse `KeyValue` `items` into `data`, `headers`, `files`,
|
||||
@ -332,7 +349,7 @@ def parse_items(items, data=None, headers=None, files=None, params=None):
|
||||
if files is None:
|
||||
files = {}
|
||||
if params is None:
|
||||
params = {}
|
||||
params = ParamDict()
|
||||
for item in items:
|
||||
value = item.value
|
||||
key = item.key
|
||||
|
@ -37,10 +37,10 @@ def get_response(args):
|
||||
try:
|
||||
credentials = None
|
||||
if args.auth:
|
||||
auth_type = (requests.auth.HTTPDigestAuth
|
||||
if args.auth_type == 'digest'
|
||||
else requests.auth.HTTPBasicAuth)
|
||||
credentials = auth_type(args.auth.key, args.auth.value)
|
||||
credentials = {
|
||||
'basic': requests.auth.HTTPBasicAuth,
|
||||
'digest': requests.auth.HTTPDigestAuth,
|
||||
}[args.auth_type](args.auth.key, args.auth.value)
|
||||
|
||||
return requests.request(
|
||||
method=args.method.lower(),
|
||||
|
@ -7,7 +7,7 @@ Many of the test cases here use httpbin.org.
|
||||
To make it run faster and offline you can::
|
||||
|
||||
# Install `httpbin` locally
|
||||
pip install httpbin
|
||||
pip install git+https://github.com/kennethreitz/httpbin.git
|
||||
|
||||
# Run it
|
||||
httpbin
|
||||
@ -15,6 +15,9 @@ To make it run faster and offline you can::
|
||||
# Run the tests against it
|
||||
HTTPBIN_URL=http://localhost:5000 python setup.py test
|
||||
|
||||
# Test all Python environments
|
||||
HTTPBIN_URL=http://localhost:5000 tox
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
@ -55,11 +58,13 @@ def httpbin(path):
|
||||
class Response(str):
|
||||
"""
|
||||
A unicode subclass holding the output of `main()`, and also
|
||||
the exit status and contents of ``stderr``.
|
||||
the exit status, the contents of ``stderr``, and de-serialized
|
||||
JSON response (if possible).
|
||||
|
||||
"""
|
||||
exit_status = None
|
||||
stderr = None
|
||||
json = None
|
||||
|
||||
|
||||
def http(*args, **kwargs):
|
||||
@ -80,7 +85,7 @@ def http(*args, **kwargs):
|
||||
stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
|
||||
stderr = kwargs['env'].stderr = tempfile.TemporaryFile()
|
||||
|
||||
exit_status = main(args=args, **kwargs)
|
||||
exit_status = main(args=['--traceback'] + list(args), **kwargs)
|
||||
|
||||
stdout.seek(0)
|
||||
stderr.seek(0)
|
||||
@ -92,6 +97,19 @@ def http(*args, **kwargs):
|
||||
stdout.close()
|
||||
stderr.close()
|
||||
|
||||
if TERMINAL_COLOR_PRESENCE_CHECK not in r:
|
||||
# De-serialize JSON body if possible.
|
||||
if r.strip().startswith('{'):
|
||||
r.json = json.loads(r)
|
||||
elif r.count('Content-Type:') == 1 and 'application/json' in r:
|
||||
try:
|
||||
j = r.strip()[r.strip().rindex('\n\n'):]
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
r.strip().index('\n')
|
||||
r.json = json.loads(j)
|
||||
|
||||
return r
|
||||
|
||||
|
||||
@ -157,6 +175,19 @@ class HTTPieTest(BaseTestCase):
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn('"foo": "bar"', r)
|
||||
|
||||
def test_POST_form_multiple_values(self):
|
||||
r = http(
|
||||
'--form',
|
||||
'POST',
|
||||
httpbin('/post'),
|
||||
'foo=bar',
|
||||
'foo=baz',
|
||||
)
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertDictEqual(r.json['form'], {
|
||||
'foo': ['bar', 'baz']
|
||||
})
|
||||
|
||||
def test_POST_stdin(self):
|
||||
|
||||
env = Environment(
|
||||
@ -490,8 +521,7 @@ class AuthTest(BaseTestCase):
|
||||
|
||||
def test_basic_auth(self):
|
||||
r = http(
|
||||
'--auth',
|
||||
'user:password',
|
||||
'--auth=user:password',
|
||||
'GET',
|
||||
httpbin('/basic-auth/user/password')
|
||||
)
|
||||
@ -502,8 +532,7 @@ class AuthTest(BaseTestCase):
|
||||
def test_digest_auth(self):
|
||||
r = http(
|
||||
'--auth-type=digest',
|
||||
'--auth',
|
||||
'user:password',
|
||||
'--auth=user:password',
|
||||
'GET',
|
||||
httpbin('/digest-auth/auth/user/password')
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user