1
0
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:
Jakub Roztocil 2012-07-24 16:46:04 +02:00
parent 9944def703
commit 7af08b6faa
5 changed files with 68 additions and 20 deletions

View File

@ -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,

View 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".

View File

@ -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

View File

@ -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(),

View File

@ -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')
)