Merge pull request #791 from sigmavirus24/key_val_lists

Key val lists
This commit is contained in:
Kenneth Reitz
2012-08-18 15:54:49 -07:00
5 changed files with 106 additions and 53 deletions
+1
View File
@@ -111,3 +111,4 @@ Patches and Suggestions
- Leila Muhtasib
- Matthias Rahlf <matthias@webding.de>
- Jakub Roztocil <jakub@roztocil.name>
- Ian Cordasco <graffatcolmingov@gmail.com> @sigmavirus24
+16 -37
View File
@@ -31,7 +31,7 @@ from .exceptions import (
from .utils import (
get_encoding_from_headers, stream_untransfer, guess_filename, requote_uri,
stream_decode_response_unicode, get_netrc_auth, get_environ_proxies,
DEFAULT_CA_BUNDLE_PATH)
to_key_val_list, DEFAULT_CA_BUNDLE_PATH)
from .compat import (
cookielib, urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes,
StringIO, is_py2, chardet, json, builtin_str, numeric_types)
@@ -41,8 +41,8 @@ CONTENT_CHUNK_SIZE = 10 * 1024
class Request(object):
"""The :class:`Request <Request>` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""The :class:`Request <Request>` object. It carries out all functionality
of Requests. Recommended interface is with the Requests functions.
"""
def __init__(self,
@@ -322,21 +322,13 @@ class Request(object):
if parameters are supplied as a dict.
"""
if isinstance(data, bytes):
return data
if isinstance(data, str):
if isinstance(data, (str, bytes)):
return data
elif hasattr(data, 'read'):
return data
elif hasattr(data, '__iter__'):
try:
dict(data)
except ValueError:
raise ValueError('Unable to encode lists with elements that are not 2-tuples.')
params = list(data.items() if isinstance(data, dict) else data)
result = []
for k, vs in params:
for k, vs in to_key_val_list(data):
for v in isinstance(vs, list) and vs or [vs]:
result.append(
(k.encode('utf-8') if isinstance(k, str) else k,
@@ -356,23 +348,11 @@ class Request(object):
if (not files) or isinstance(self.data, str):
return None
def tuples(obj):
"""Ensure 2-tuples. A dict or a 2-tuples list can be supplied."""
if isinstance(obj, dict):
return list(obj.items())
elif hasattr(obj, '__iter__'):
try:
dict(obj)
except ValueError:
pass
else:
return obj
raise ValueError('A dict or a list of 2-tuples required.')
new_fields = []
fields = to_key_val_list(self.data)
files = to_key_val_list(files)
# 2-tuples containing both file and data fields.
fields = []
for k, v in tuples(files):
for (k, v) in files:
# support for explicit filename
if isinstance(v, (tuple, list)):
fn, fp = v
@@ -383,16 +363,15 @@ class Request(object):
fp = StringIO(fp)
if isinstance(fp, bytes):
fp = BytesIO(fp)
fields.append((k, (fn, fp.read())))
new_fields.append((k, (fn, fp.read())))
for k, vs in tuples(self.data):
if isinstance(vs, list):
for v in vs:
fields.append((k, str(v)))
for field, val in fields:
if isinstance(val, list):
for v in val:
new_fields.append((k, str(v)))
else:
fields.append((k, str(vs)))
body, content_type = encode_multipart_formdata(fields)
new_fields.append((field, str(val)))
body, content_type = encode_multipart_formdata(new_fields)
return body, content_type
+13 -8
View File
@@ -15,7 +15,7 @@ from .cookies import cookiejar_from_dict, remove_cookie_by_name
from .defaults import defaults
from .models import Request
from .hooks import dispatch_hook
from .utils import header_expand
from .utils import header_expand, to_key_val_list
from .packages.urllib3.poolmanager import PoolManager
@@ -38,12 +38,14 @@ def merge_kwargs(local_kwarg, default_kwarg):
if not hasattr(default_kwarg, 'items'):
return local_kwarg
local_kwarg = to_key_val_list(local_kwarg)
# Update new values.
kwargs = default_kwarg.copy()
kwargs.update(local_kwarg)
# Remove keys that are set to None.
for (k, v) in list(local_kwarg.items()):
for (k, v) in local_kwarg:
if v is None:
del kwargs[k]
@@ -70,12 +72,13 @@ class Session(object):
verify=True,
cert=None):
#self.headers = to_key_val_list(headers or [])
self.headers = headers or {}
self.auth = auth
self.timeout = timeout
self.proxies = proxies or {}
self.proxies = to_key_val_list(proxies or [])
self.hooks = hooks or {}
self.params = params or {}
self.params = to_key_val_list(params or [])
self.config = config or {}
self.prefetch = prefetch
self.verify = verify
@@ -156,10 +159,10 @@ class Session(object):
method = str(method).upper()
# Default empty dicts for dict params.
data = {} if data is None else data
files = {} if files is None else files
data = [] if data is None else data
files = [] if files is None else files
headers = {} if headers is None else headers
params = {} if params is None else params
params = [] if params is None else params
hooks = {} if hooks is None else hooks
prefetch = prefetch if prefetch is not None else self.prefetch
@@ -169,6 +172,8 @@ class Session(object):
# Expand header values.
if headers:
#e = [(k, header_expand(v)) for k, v in to_key_val_list(headers)]
#headers = e
for k, v in list(headers.items()) or {}:
headers[k] = header_expand(v)
@@ -184,7 +189,7 @@ class Session(object):
hooks=hooks,
timeout=timeout,
allow_redirects=allow_redirects,
proxies=proxies,
proxies=to_key_val_list(proxies),
config=config,
prefetch=prefetch,
verify=verify,
+27
View File
@@ -114,6 +114,33 @@ def guess_filename(obj):
return name
def to_key_val_list(value):
"""Take an object and test to see if it can be represented as a
dictionary. Unless it can not be represented as such, return a list of
tuples, e.g.,:
>>> to_key_val_list([('key', 'val')])
[('key', 'val')]
>>> to_key_val_list('string')
ValueError: ...
>>> to_key_val_list({'key': 'val'})
[('key', 'val')]
"""
if value is None:
return None
try:
dict(value)
except ValueError:
raise ValueError('Unable to encode lists with elements that are not '
'2-tuples.')
if isinstance(value, dict) or hasattr(value, 'items'):
value = value.items()
return list(value)
# From mitsuhiko/werkzeug (used with permission).
def parse_list_header(value):
"""Parse lists as described by RFC 2068 Section 2.
+49 -8
View File
@@ -52,7 +52,6 @@ class TestSetup(object):
# time.sleep(1)
_httpbin = True
class TestBaseMixin(object):
def assertCookieHas(self, cookie, **kwargs):
@@ -62,7 +61,6 @@ class TestBaseMixin(object):
message = 'Failed comparison for %s: %s != %s' % (attr, cookie_attr, expected_value)
self.assertEqual(cookie_attr, expected_value, message)
class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
"""Requests test cases."""
@@ -95,6 +93,11 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
self.assertEqual(request.full_url,
"http://example.com/path?key=value&a=b#fragment")
def test_params_accepts_kv_list(self):
request = requests.Request('http://example.com/path',
params=[('a', 'b')])
self.assertEqual(request.full_url, 'http://example.com/path?a=b')
def test_HTTP_200_OK_GET(self):
r = get(httpbin('get'))
self.assertEqual(r.status_code, 200)
@@ -311,11 +314,18 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
with open(__file__) as f:
post2 = post(url, files={'some': f})
post3 = post(url, files=[('some', f)])
self.assertEqual(post2.status_code, 200)
post3 = post(url, data='[{"some": "json"}]')
self.assertEqual(post3.status_code, 200)
post4 = post(url, data='[{"some": "json"}]')
self.assertEqual(post4.status_code, 200)
try:
post(url, files=['bad file data'])
except ValueError:
pass
def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
for service in SERVICES:
@@ -325,8 +335,13 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
post1 = post(url,
files={'some': f},
data={'some': 'data'})
post2 = post(url, data={'some': 'data'}, files=[('some', f)])
post3 = post(url, data=[('some', 'data')],
files=[('some', f)])
self.assertEqual(post1.status_code, 200)
self.assertEqual(post2.status_code, 200)
self.assertEqual(post3.status_code, 200)
def test_POSTBIN_GET_POST_FILES_WITH_HEADERS(self):
@@ -351,10 +366,12 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
post1 = post(url, files={'fname.txt': 'fdata'})
self.assertEqual(post1.status_code, 200)
post2 = post(url, files={'fname.txt': 'fdata', 'fname2.txt': 'more fdata'})
post2 = post(url, files={'fname.txt': 'fdata',
'fname2.txt': 'more fdata'})
self.assertEqual(post2.status_code, 200)
post3 = post(url, files={'fname.txt': 'fdata', 'fname2.txt': open(__file__, 'rb')})
post3 = post(url, files={'fname.txt': 'fdata',
'fname2.txt': open(__file__, 'rb')})
self.assertEqual(post3.status_code, 200)
post4 = post(url, files={'fname.txt': 'fdata'})
@@ -376,9 +393,30 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
post7 = post(url, files={'fname.txt': 'fdata to verify'})
rbody = json.loads(post7.text)
self.assertTrue(rbody.get('files', None))
self.assertTrue(rbody['files'].get('fname.txt'), None)
self.assertTrue(rbody['files'].get('fname.txt', None))
self.assertEqual(rbody['files']['fname.txt'], 'fdata to verify')
post8 = post(url, files=[('fname.txt', 'fdata')])
self.assertEqual(post8.status_code, 200)
resp_body = post8.json
self.assertTrue(resp_body.get('files', None))
self.assertTrue(resp_body['files'].get('fname.txt', None))
self.assertEqual(resp_body['files']['fname.txt'], 'fdata')
post9 = post(url, files=[('fname.txt', fdata)])
self.assertEqual(post9.status_code, 200)
post10 = post(url, files=[('file',
('file.txt', 'more file data'))])
self.assertEqual(post10.status_code, 200)
post11 = post(url, files=[('fname.txt', 'fdata'),
('fname2.txt', 'more fdata')])
post12 = post(url, files=[('fname.txt', 'fdata'),
('fname2.txt', open(__file__, 'rb'))])
self.assertEqual(post11.status_code, 200)
self.assertEqual(post12.status_code, 200)
def test_nonzero_evaluation(self):
for service in SERVICES:
@@ -933,13 +971,14 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
# Don't choke on headers with none in the value.
requests.get(httpbin('headers'), headers={'Foo': None})
except TypeError:
self.fail()
self.fail('Not able to have none in header values')
def test_danger_mode_redirects(self):
s = requests.session()
s.config['danger_mode'] = True
s.get(httpbin('redirect', '4'))
def test_empty_response(self):
r = requests.get(httpbin('status', '404'))
r.text
@@ -985,6 +1024,8 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase):
t = json.loads(r.text)
self.assertEqual(t.get('form'), {'field': ['a', 'b']})
self.assertEqual(t.get('files'), files)
r = post(httpbin('post'), data=data, files=files.items())
self.assertEqual(t.get('files'), files)
def test_str_data_content_type(self):
data = 'test string data'