aboutsummaryrefslogtreecommitdiff
path: root/requests/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests/models.py')
-rw-r--r--requests/models.py101
1 files changed, 64 insertions, 37 deletions
diff --git a/requests/models.py b/requests/models.py
index 70e3503..60f58d2 100644
--- a/requests/models.py
+++ b/requests/models.py
@@ -16,7 +16,7 @@ from .status_codes import codes
from .auth import HTTPBasicAuth, HTTPProxyAuth
from .packages.urllib3.response import HTTPResponse
-from .packages.urllib3.exceptions import MaxRetryError
+from .packages.urllib3.exceptions import MaxRetryError, LocationParseError
from .packages.urllib3.exceptions import SSLError as _SSLError
from .packages.urllib3.exceptions import HTTPError as _HTTPError
from .packages.urllib3 import connectionpool, poolmanager
@@ -24,10 +24,11 @@ from .packages.urllib3.filepost import encode_multipart_formdata
from .defaults import SCHEMAS
from .exceptions import (
ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects,
- URLRequired, SSLError, MissingSchema, InvalidSchema)
+ URLRequired, SSLError, MissingSchema, InvalidSchema, InvalidURL)
from .utils import (
get_encoding_from_headers, stream_untransfer, guess_filename, requote_uri,
- dict_from_string, stream_decode_response_unicode, get_netrc_auth)
+ dict_from_string, stream_decode_response_unicode, get_netrc_auth,
+ DEFAULT_CA_BUNDLE_PATH)
from .compat import (
urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes,
SimpleCookie, is_py2)
@@ -61,6 +62,7 @@ class Request(object):
proxies=None,
hooks=None,
config=None,
+ prefetch=False,
_poolmanager=None,
verify=None,
session=None,
@@ -80,7 +82,7 @@ class Request(object):
self.headers = dict(headers or [])
#: Dictionary of files to multipart upload (``{filename: content}``).
- self.files = files
+ self.files = None
#: HTTP Method to use.
self.method = method
@@ -90,7 +92,8 @@ class Request(object):
self.data = None
#: Dictionary or byte of querystring data to attach to the
- #: :class:`Request <Request>`.
+ #: :class:`Request <Request>`. The dictionary values can be lists for representing
+ #: multivalued query parameters.
self.params = None
#: True if :class:`Request <Request>` is part of a redirect chain (disables history
@@ -113,6 +116,7 @@ class Request(object):
self.data, self._enc_data = self._encode_params(data)
self.params, self._enc_params = self._encode_params(params)
+ self.files, self._enc_files = self._encode_files(files)
#: :class:`Response <Response>` instance, containing
#: content and metadata of HTTP Response, once :attr:`sent <send>`.
@@ -147,6 +151,9 @@ class Request(object):
#: SSL Certificate
self.cert = cert
+ #: Prefetch response content
+ self.prefetch = prefetch
+
if headers:
headers = CaseInsensitiveDict(self.headers)
else:
@@ -328,6 +335,29 @@ class Request(object):
else:
return data, data
+ def _encode_files(self,files):
+
+ if (not files) or isinstance(self.data, str):
+ return None, None
+
+ try:
+ fields = self.data.copy()
+ except AttributeError:
+ fields = dict(self.data)
+
+ for (k, v) in list(files.items()):
+ # support for explicit filename
+ if isinstance(v, (tuple, list)):
+ fn, fp = v
+ else:
+ fn = guess_filename(v) or k
+ fp = v
+ fields.update({k: (fn, fp.read())})
+
+ (body, content_type) = encode_multipart_formdata(fields)
+
+ return files, (body, content_type)
+
@property
def full_url(self):
"""Build the actual URL to use."""
@@ -407,7 +437,18 @@ class Request(object):
def register_hook(self, event, hook):
"""Properly register a hook."""
- return self.hooks[event].append(hook)
+ self.hooks[event].append(hook)
+
+ def deregister_hook(self,event,hook):
+ """Deregister a previously registered hook.
+ Returns True if the hook existed, False if not.
+ """
+
+ try:
+ self.hooks[event].remove(hook)
+ return True
+ except ValueError:
+ return False
def send(self, anyway=False, prefetch=False):
"""Sends the request. Returns True of successful, False if not.
@@ -435,26 +476,7 @@ class Request(object):
# Multi-part file uploads.
if self.files:
- if not isinstance(self.data, str):
-
- try:
- fields = self.data.copy()
- except AttributeError:
- fields = dict(self.data)
-
- for (k, v) in list(self.files.items()):
- # support for explicit filename
- if isinstance(v, (tuple, list)):
- fn, fp = v
- else:
- fn = guess_filename(v) or k
- fp = v
- fields.update({k: (fn, fp.read())})
-
- (body, content_type) = encode_multipart_formdata(fields)
- else:
- pass
- # TODO: Conflict?
+ (body, content_type) = self._enc_files
else:
if self.data:
@@ -496,10 +518,13 @@ class Request(object):
self.__dict__.update(r.__dict__)
else:
# Check to see if keep_alive is allowed.
- if self.config.get('keep_alive'):
- conn = self._poolmanager.connection_from_url(url)
- else:
- conn = connectionpool.connection_from_url(url)
+ try:
+ if self.config.get('keep_alive'):
+ conn = self._poolmanager.connection_from_url(url)
+ else:
+ conn = connectionpool.connection_from_url(url)
+ except LocationParseError as e:
+ raise InvalidURL(e)
if url.startswith('https') and self.verify:
@@ -513,13 +538,15 @@ class Request(object):
if not cert_loc and self.config.get('trust_env'):
cert_loc = os.environ.get('REQUESTS_CA_BUNDLE')
- # Curl compatiblity.
+ # Curl compatibility.
if not cert_loc and self.config.get('trust_env'):
cert_loc = os.environ.get('CURL_CA_BUNDLE')
- # Use the awesome certifi list.
if not cert_loc:
- cert_loc = __import__('certifi').where()
+ cert_loc = DEFAULT_CA_BUNDLE_PATH
+
+ if not cert_loc:
+ raise Exception("Could not find a suitable SSL CA certificate bundle.")
conn.cert_reqs = 'CERT_REQUIRED'
conn.ca_certs = cert_loc
@@ -604,7 +631,7 @@ class Request(object):
self.__dict__.update(r.__dict__)
# If prefetch is True, mark content as consumed.
- if prefetch:
+ if prefetch or self.prefetch:
# Save the response.
self.response.content
@@ -623,7 +650,7 @@ class Response(object):
def __init__(self):
- self._content = None
+ self._content = False
self._content_consumed = False
#: Integer Code of responded HTTP Status.
@@ -679,7 +706,7 @@ class Response(object):
return False
return True
- def iter_content(self, chunk_size=10 * 1024, decode_unicode=False):
+ def iter_content(self, chunk_size=1, decode_unicode=False):
"""Iterates over the response data. This avoids reading the content
at once into memory for large responses. The chunk size is the number
of bytes it should read into memory. This is not necessarily the
@@ -736,7 +763,7 @@ class Response(object):
def content(self):
"""Content of the response, in bytes."""
- if self._content is None:
+ if self._content is False:
# Read the contents.
try:
if self._content_consumed: