Index: third_party/requests/models.py |
diff --git a/third_party/requests/models.py b/third_party/requests/models.py |
index 6cf2aaa1a6c8b69dd7b5f8d7e26066e4055c9883..8fd973535c33c6bc07bf947c893a81da71dee91f 100644 |
--- a/third_party/requests/models.py |
+++ b/third_party/requests/models.py |
@@ -11,7 +11,7 @@ import collections |
import logging |
import datetime |
-from io import BytesIO |
+from io import BytesIO, UnsupportedOperation |
from .hooks import default_hooks |
from .structures import CaseInsensitiveDict |
@@ -19,14 +19,16 @@ from .auth import HTTPBasicAuth |
from .cookies import cookiejar_from_dict, get_cookie_header |
from .packages.urllib3.filepost import encode_multipart_formdata |
from .packages.urllib3.util import parse_url |
-from .exceptions import HTTPError, RequestException, MissingSchema, InvalidURL |
+from .exceptions import ( |
+ HTTPError, RequestException, MissingSchema, InvalidURL, |
+ ChunkedEncodingError) |
from .utils import ( |
guess_filename, get_auth_from_url, requote_uri, |
stream_decode_response_unicode, to_key_val_list, parse_header_links, |
- iter_slices, guess_json_utf, super_len) |
+ iter_slices, guess_json_utf, super_len, to_native_string) |
from .compat import ( |
- cookielib, urlparse, urlunparse, urlsplit, urlencode, str, bytes, StringIO, |
- is_py2, chardet, json, builtin_str, basestring) |
+ cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, |
+ is_py2, chardet, json, builtin_str, basestring, IncompleteRead) |
CONTENT_CHUNK_SIZE = 10 * 1024 |
ITER_CHUNK_SIZE = 512 |
@@ -92,8 +94,10 @@ class RequestEncodingMixin(object): |
if parameters are supplied as a dict. |
""" |
- if (not files) or isinstance(data, str): |
- return None |
+ if (not files): |
+ raise ValueError("Files must be provided.") |
+ elif isinstance(data, basestring): |
+ raise ValueError("Data must not be a string.") |
new_fields = [] |
fields = to_key_val_list(data or {}) |
@@ -104,6 +108,10 @@ class RequestEncodingMixin(object): |
val = [val] |
for v in val: |
if v is not None: |
+ # Don't call str() on bytestrings: in Py3 it all goes wrong. |
+ if not isinstance(v, bytes): |
+ v = str(v) |
+ |
new_fields.append( |
(field.decode('utf-8') if isinstance(field, bytes) else field, |
v.encode('utf-8') if isinstance(v, str) else v)) |
@@ -139,6 +147,9 @@ class RequestHooksMixin(object): |
def register_hook(self, event, hook): |
"""Properly register a hook.""" |
+ if event not in self.hooks: |
+ raise ValueError('Unsupported event specified, with event name "%s"' % (event)) |
+ |
if isinstance(hook, collections.Callable): |
self.hooks[event].append(hook) |
elif hasattr(hook, '__iter__'): |
@@ -184,8 +195,8 @@ class Request(RequestHooksMixin): |
url=None, |
headers=None, |
files=None, |
- data=dict(), |
- params=dict(), |
+ data=None, |
+ params=None, |
auth=None, |
cookies=None, |
hooks=None): |
@@ -209,7 +220,6 @@ class Request(RequestHooksMixin): |
self.params = params |
self.auth = auth |
self.cookies = cookies |
- self.hooks = hooks |
def __repr__(self): |
return '<Request [%s]>' % (self.method) |
@@ -217,19 +227,17 @@ class Request(RequestHooksMixin): |
def prepare(self): |
"""Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it.""" |
p = PreparedRequest() |
- |
- p.prepare_method(self.method) |
- p.prepare_url(self.url, self.params) |
- p.prepare_headers(self.headers) |
- p.prepare_cookies(self.cookies) |
- p.prepare_body(self.data, self.files) |
- p.prepare_auth(self.auth, self.url) |
- # Note that prepare_auth must be last to enable authentication schemes |
- # such as OAuth to work on a fully prepared request. |
- |
- # This MUST go after prepare_auth. Authenticators could add a hook |
- p.prepare_hooks(self.hooks) |
- |
+ p.prepare( |
+ method=self.method, |
+ url=self.url, |
+ headers=self.headers, |
+ files=self.files, |
+ data=self.data, |
+ params=self.params, |
+ auth=self.auth, |
+ cookies=self.cookies, |
+ hooks=self.hooks, |
+ ) |
return p |
@@ -264,9 +272,34 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
#: dictionary of callback hooks, for internal usage. |
self.hooks = default_hooks() |
+ def prepare(self, method=None, url=None, headers=None, files=None, |
+ data=None, params=None, auth=None, cookies=None, hooks=None): |
+ """Prepares the the entire request with the given parameters.""" |
+ |
+ self.prepare_method(method) |
+ self.prepare_url(url, params) |
+ self.prepare_headers(headers) |
+ self.prepare_cookies(cookies) |
+ self.prepare_body(data, files) |
+ self.prepare_auth(auth, url) |
+ # Note that prepare_auth must be last to enable authentication schemes |
+ # such as OAuth to work on a fully prepared request. |
+ |
+ # This MUST go after prepare_auth. Authenticators could add a hook |
+ self.prepare_hooks(hooks) |
+ |
def __repr__(self): |
return '<PreparedRequest [%s]>' % (self.method) |
+ def copy(self): |
+ p = PreparedRequest() |
+ p.method = self.method |
+ p.url = self.url |
+ p.headers = self.headers |
+ p.body = self.body |
+ p.hooks = self.hooks |
+ return p |
+ |
def prepare_method(self, method): |
"""Prepares the given HTTP method.""" |
self.method = method |
@@ -337,8 +370,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
"""Prepares the given HTTP headers.""" |
if headers: |
- headers = dict((name.encode('ascii'), value) for name, value in headers.items()) |
- self.headers = CaseInsensitiveDict(headers) |
+ self.headers = CaseInsensitiveDict((to_native_string(name), value) for name, value in headers.items()) |
else: |
self.headers = CaseInsensitiveDict() |
@@ -352,7 +384,6 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
body = None |
content_type = None |
length = None |
- is_stream = False |
is_stream = all([ |
hasattr(data, '__iter__'), |
@@ -363,8 +394,8 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
try: |
length = super_len(data) |
- except (TypeError, AttributeError): |
- length = False |
+ except (TypeError, AttributeError, UnsupportedOperation): |
+ length = None |
if is_stream: |
body = data |
@@ -372,13 +403,10 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
if files: |
raise NotImplementedError('Streamed bodies and files are mutually exclusive.') |
- if length: |
+ if length is not None: |
self.headers['Content-Length'] = str(length) |
else: |
self.headers['Transfer-Encoding'] = 'chunked' |
- # Check if file, fo, generator, iterator. |
- # If not, run through normal process. |
- |
else: |
# Multi-part file uploads. |
if files: |
@@ -537,11 +565,22 @@ class Response(object): |
return iter_slices(self._content, chunk_size) |
def generate(): |
- while 1: |
- chunk = self.raw.read(chunk_size, decode_content=True) |
- if not chunk: |
- break |
- yield chunk |
+ try: |
+ # Special case for urllib3. |
+ try: |
+ for chunk in self.raw.stream(chunk_size, |
+ decode_content=True): |
+ yield chunk |
+ except IncompleteRead as e: |
+ raise ChunkedEncodingError(e) |
+ except AttributeError: |
+ # Standard file-like object. |
+ while 1: |
+ chunk = self.raw.read(chunk_size) |
+ if not chunk: |
+ break |
+ yield chunk |
+ |
self._content_consumed = True |
gen = generate() |
@@ -683,4 +722,9 @@ class Response(object): |
raise HTTPError(http_error_msg, response=self) |
def close(self): |
+ """Closes the underlying file descriptor and releases the connection |
+ back to the pool. |
+ |
+ *Note: Should not normally need to be called explicitly.* |
+ """ |
return self.raw.release_conn() |