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