mirror of
https://github.com/kennethreitz/requests3.git
synced 2026-06-05 23:10:16 +00:00
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user