| OLD | NEW |
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 2 | 2 |
| 3 """ | 3 """ |
| 4 requests.models | 4 requests.models |
| 5 ~~~~~~~~~~~~~~~ | 5 ~~~~~~~~~~~~~~~ |
| 6 | 6 |
| 7 This module contains the primary objects that power Requests. | 7 This module contains the primary objects that power Requests. |
| 8 """ | 8 """ |
| 9 | 9 |
| 10 import collections | 10 import collections |
| 11 import logging | 11 import logging |
| 12 import datetime | 12 import datetime |
| 13 | 13 |
| 14 from io import BytesIO | 14 from io import BytesIO, UnsupportedOperation |
| 15 from .hooks import default_hooks | 15 from .hooks import default_hooks |
| 16 from .structures import CaseInsensitiveDict | 16 from .structures import CaseInsensitiveDict |
| 17 | 17 |
| 18 from .auth import HTTPBasicAuth | 18 from .auth import HTTPBasicAuth |
| 19 from .cookies import cookiejar_from_dict, get_cookie_header | 19 from .cookies import cookiejar_from_dict, get_cookie_header |
| 20 from .packages.urllib3.filepost import encode_multipart_formdata | 20 from .packages.urllib3.filepost import encode_multipart_formdata |
| 21 from .packages.urllib3.util import parse_url | 21 from .packages.urllib3.util import parse_url |
| 22 from .exceptions import HTTPError, RequestException, MissingSchema, InvalidURL | 22 from .exceptions import ( |
| 23 HTTPError, RequestException, MissingSchema, InvalidURL, |
| 24 ChunkedEncodingError) |
| 23 from .utils import ( | 25 from .utils import ( |
| 24 guess_filename, get_auth_from_url, requote_uri, | 26 guess_filename, get_auth_from_url, requote_uri, |
| 25 stream_decode_response_unicode, to_key_val_list, parse_header_links, | 27 stream_decode_response_unicode, to_key_val_list, parse_header_links, |
| 26 iter_slices, guess_json_utf, super_len) | 28 iter_slices, guess_json_utf, super_len, to_native_string) |
| 27 from .compat import ( | 29 from .compat import ( |
| 28 cookielib, urlparse, urlunparse, urlsplit, urlencode, str, bytes, StringIO, | 30 cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO, |
| 29 is_py2, chardet, json, builtin_str, basestring) | 31 is_py2, chardet, json, builtin_str, basestring, IncompleteRead) |
| 30 | 32 |
| 31 CONTENT_CHUNK_SIZE = 10 * 1024 | 33 CONTENT_CHUNK_SIZE = 10 * 1024 |
| 32 ITER_CHUNK_SIZE = 512 | 34 ITER_CHUNK_SIZE = 512 |
| 33 | 35 |
| 34 log = logging.getLogger(__name__) | 36 log = logging.getLogger(__name__) |
| 35 | 37 |
| 36 | 38 |
| 37 class RequestEncodingMixin(object): | 39 class RequestEncodingMixin(object): |
| 38 @property | 40 @property |
| 39 def path_url(self): | 41 def path_url(self): |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 | 87 |
| 86 @staticmethod | 88 @staticmethod |
| 87 def _encode_files(files, data): | 89 def _encode_files(files, data): |
| 88 """Build the body for a multipart/form-data request. | 90 """Build the body for a multipart/form-data request. |
| 89 | 91 |
| 90 Will successfully encode files when passed as a dict or a list of | 92 Will successfully encode files when passed as a dict or a list of |
| 91 2-tuples. Order is retained if data is a list of 2-tuples but abritrary | 93 2-tuples. Order is retained if data is a list of 2-tuples but abritrary |
| 92 if parameters are supplied as a dict. | 94 if parameters are supplied as a dict. |
| 93 | 95 |
| 94 """ | 96 """ |
| 95 if (not files) or isinstance(data, str): | 97 if (not files): |
| 96 return None | 98 raise ValueError("Files must be provided.") |
| 99 elif isinstance(data, basestring): |
| 100 raise ValueError("Data must not be a string.") |
| 97 | 101 |
| 98 new_fields = [] | 102 new_fields = [] |
| 99 fields = to_key_val_list(data or {}) | 103 fields = to_key_val_list(data or {}) |
| 100 files = to_key_val_list(files or {}) | 104 files = to_key_val_list(files or {}) |
| 101 | 105 |
| 102 for field, val in fields: | 106 for field, val in fields: |
| 103 if isinstance(val, basestring) or not hasattr(val, '__iter__'): | 107 if isinstance(val, basestring) or not hasattr(val, '__iter__'): |
| 104 val = [val] | 108 val = [val] |
| 105 for v in val: | 109 for v in val: |
| 106 if v is not None: | 110 if v is not None: |
| 111 # Don't call str() on bytestrings: in Py3 it all goes wrong. |
| 112 if not isinstance(v, bytes): |
| 113 v = str(v) |
| 114 |
| 107 new_fields.append( | 115 new_fields.append( |
| 108 (field.decode('utf-8') if isinstance(field, bytes) else
field, | 116 (field.decode('utf-8') if isinstance(field, bytes) else
field, |
| 109 v.encode('utf-8') if isinstance(v, str) else v)) | 117 v.encode('utf-8') if isinstance(v, str) else v)) |
| 110 | 118 |
| 111 for (k, v) in files: | 119 for (k, v) in files: |
| 112 # support for explicit filename | 120 # support for explicit filename |
| 113 ft = None | 121 ft = None |
| 114 if isinstance(v, (tuple, list)): | 122 if isinstance(v, (tuple, list)): |
| 115 if len(v) == 2: | 123 if len(v) == 2: |
| 116 fn, fp = v | 124 fn, fp = v |
| (...skipping 15 matching lines...) Expand all Loading... |
| 132 | 140 |
| 133 body, content_type = encode_multipart_formdata(new_fields) | 141 body, content_type = encode_multipart_formdata(new_fields) |
| 134 | 142 |
| 135 return body, content_type | 143 return body, content_type |
| 136 | 144 |
| 137 | 145 |
| 138 class RequestHooksMixin(object): | 146 class RequestHooksMixin(object): |
| 139 def register_hook(self, event, hook): | 147 def register_hook(self, event, hook): |
| 140 """Properly register a hook.""" | 148 """Properly register a hook.""" |
| 141 | 149 |
| 150 if event not in self.hooks: |
| 151 raise ValueError('Unsupported event specified, with event name "%s"'
% (event)) |
| 152 |
| 142 if isinstance(hook, collections.Callable): | 153 if isinstance(hook, collections.Callable): |
| 143 self.hooks[event].append(hook) | 154 self.hooks[event].append(hook) |
| 144 elif hasattr(hook, '__iter__'): | 155 elif hasattr(hook, '__iter__'): |
| 145 self.hooks[event].extend(h for h in hook if isinstance(h, collection
s.Callable)) | 156 self.hooks[event].extend(h for h in hook if isinstance(h, collection
s.Callable)) |
| 146 | 157 |
| 147 def deregister_hook(self, event, hook): | 158 def deregister_hook(self, event, hook): |
| 148 """Deregister a previously registered hook. | 159 """Deregister a previously registered hook. |
| 149 Returns True if the hook existed, False if not. | 160 Returns True if the hook existed, False if not. |
| 150 """ | 161 """ |
| 151 | 162 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 177 >>> req = requests.Request('GET', 'http://httpbin.org/get') | 188 >>> req = requests.Request('GET', 'http://httpbin.org/get') |
| 178 >>> req.prepare() | 189 >>> req.prepare() |
| 179 <PreparedRequest [GET]> | 190 <PreparedRequest [GET]> |
| 180 | 191 |
| 181 """ | 192 """ |
| 182 def __init__(self, | 193 def __init__(self, |
| 183 method=None, | 194 method=None, |
| 184 url=None, | 195 url=None, |
| 185 headers=None, | 196 headers=None, |
| 186 files=None, | 197 files=None, |
| 187 data=dict(), | 198 data=None, |
| 188 params=dict(), | 199 params=None, |
| 189 auth=None, | 200 auth=None, |
| 190 cookies=None, | 201 cookies=None, |
| 191 hooks=None): | 202 hooks=None): |
| 192 | 203 |
| 193 # Default empty dicts for dict params. | 204 # Default empty dicts for dict params. |
| 194 data = [] if data is None else data | 205 data = [] if data is None else data |
| 195 files = [] if files is None else files | 206 files = [] if files is None else files |
| 196 headers = {} if headers is None else headers | 207 headers = {} if headers is None else headers |
| 197 params = {} if params is None else params | 208 params = {} if params is None else params |
| 198 hooks = {} if hooks is None else hooks | 209 hooks = {} if hooks is None else hooks |
| 199 | 210 |
| 200 self.hooks = default_hooks() | 211 self.hooks = default_hooks() |
| 201 for (k, v) in list(hooks.items()): | 212 for (k, v) in list(hooks.items()): |
| 202 self.register_hook(event=k, hook=v) | 213 self.register_hook(event=k, hook=v) |
| 203 | 214 |
| 204 self.method = method | 215 self.method = method |
| 205 self.url = url | 216 self.url = url |
| 206 self.headers = headers | 217 self.headers = headers |
| 207 self.files = files | 218 self.files = files |
| 208 self.data = data | 219 self.data = data |
| 209 self.params = params | 220 self.params = params |
| 210 self.auth = auth | 221 self.auth = auth |
| 211 self.cookies = cookies | 222 self.cookies = cookies |
| 212 self.hooks = hooks | |
| 213 | 223 |
| 214 def __repr__(self): | 224 def __repr__(self): |
| 215 return '<Request [%s]>' % (self.method) | 225 return '<Request [%s]>' % (self.method) |
| 216 | 226 |
| 217 def prepare(self): | 227 def prepare(self): |
| 218 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmiss
ion and returns it.""" | 228 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmiss
ion and returns it.""" |
| 219 p = PreparedRequest() | 229 p = PreparedRequest() |
| 220 | 230 p.prepare( |
| 221 p.prepare_method(self.method) | 231 method=self.method, |
| 222 p.prepare_url(self.url, self.params) | 232 url=self.url, |
| 223 p.prepare_headers(self.headers) | 233 headers=self.headers, |
| 224 p.prepare_cookies(self.cookies) | 234 files=self.files, |
| 225 p.prepare_body(self.data, self.files) | 235 data=self.data, |
| 226 p.prepare_auth(self.auth, self.url) | 236 params=self.params, |
| 227 # Note that prepare_auth must be last to enable authentication schemes | 237 auth=self.auth, |
| 228 # such as OAuth to work on a fully prepared request. | 238 cookies=self.cookies, |
| 229 | 239 hooks=self.hooks, |
| 230 # This MUST go after prepare_auth. Authenticators could add a hook | 240 ) |
| 231 p.prepare_hooks(self.hooks) | |
| 232 | |
| 233 return p | 241 return p |
| 234 | 242 |
| 235 | 243 |
| 236 class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): | 244 class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): |
| 237 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, | 245 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, |
| 238 containing the exact bytes that will be sent to the server. | 246 containing the exact bytes that will be sent to the server. |
| 239 | 247 |
| 240 Generated from either a :class:`Request <Request>` object or manually. | 248 Generated from either a :class:`Request <Request>` object or manually. |
| 241 | 249 |
| 242 Usage:: | 250 Usage:: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 257 self.method = None | 265 self.method = None |
| 258 #: HTTP URL to send the request to. | 266 #: HTTP URL to send the request to. |
| 259 self.url = None | 267 self.url = None |
| 260 #: dictionary of HTTP headers. | 268 #: dictionary of HTTP headers. |
| 261 self.headers = None | 269 self.headers = None |
| 262 #: request body to send to the server. | 270 #: request body to send to the server. |
| 263 self.body = None | 271 self.body = None |
| 264 #: dictionary of callback hooks, for internal usage. | 272 #: dictionary of callback hooks, for internal usage. |
| 265 self.hooks = default_hooks() | 273 self.hooks = default_hooks() |
| 266 | 274 |
| 275 def prepare(self, method=None, url=None, headers=None, files=None, |
| 276 data=None, params=None, auth=None, cookies=None, hooks=None): |
| 277 """Prepares the the entire request with the given parameters.""" |
| 278 |
| 279 self.prepare_method(method) |
| 280 self.prepare_url(url, params) |
| 281 self.prepare_headers(headers) |
| 282 self.prepare_cookies(cookies) |
| 283 self.prepare_body(data, files) |
| 284 self.prepare_auth(auth, url) |
| 285 # Note that prepare_auth must be last to enable authentication schemes |
| 286 # such as OAuth to work on a fully prepared request. |
| 287 |
| 288 # This MUST go after prepare_auth. Authenticators could add a hook |
| 289 self.prepare_hooks(hooks) |
| 290 |
| 267 def __repr__(self): | 291 def __repr__(self): |
| 268 return '<PreparedRequest [%s]>' % (self.method) | 292 return '<PreparedRequest [%s]>' % (self.method) |
| 269 | 293 |
| 294 def copy(self): |
| 295 p = PreparedRequest() |
| 296 p.method = self.method |
| 297 p.url = self.url |
| 298 p.headers = self.headers |
| 299 p.body = self.body |
| 300 p.hooks = self.hooks |
| 301 return p |
| 302 |
| 270 def prepare_method(self, method): | 303 def prepare_method(self, method): |
| 271 """Prepares the given HTTP method.""" | 304 """Prepares the given HTTP method.""" |
| 272 self.method = method | 305 self.method = method |
| 273 if self.method is not None: | 306 if self.method is not None: |
| 274 self.method = self.method.upper() | 307 self.method = self.method.upper() |
| 275 | 308 |
| 276 def prepare_url(self, url, params): | 309 def prepare_url(self, url, params): |
| 277 """Prepares the given HTTP URL.""" | 310 """Prepares the given HTTP URL.""" |
| 278 #: Accept objects that have string representations. | 311 #: Accept objects that have string representations. |
| 279 try: | 312 try: |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 330 else: | 363 else: |
| 331 query = enc_params | 364 query = enc_params |
| 332 | 365 |
| 333 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragmen
t])) | 366 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragmen
t])) |
| 334 self.url = url | 367 self.url = url |
| 335 | 368 |
| 336 def prepare_headers(self, headers): | 369 def prepare_headers(self, headers): |
| 337 """Prepares the given HTTP headers.""" | 370 """Prepares the given HTTP headers.""" |
| 338 | 371 |
| 339 if headers: | 372 if headers: |
| 340 headers = dict((name.encode('ascii'), value) for name, value in head
ers.items()) | 373 self.headers = CaseInsensitiveDict((to_native_string(name), value) f
or name, value in headers.items()) |
| 341 self.headers = CaseInsensitiveDict(headers) | |
| 342 else: | 374 else: |
| 343 self.headers = CaseInsensitiveDict() | 375 self.headers = CaseInsensitiveDict() |
| 344 | 376 |
| 345 def prepare_body(self, data, files): | 377 def prepare_body(self, data, files): |
| 346 """Prepares the given HTTP body data.""" | 378 """Prepares the given HTTP body data.""" |
| 347 | 379 |
| 348 # Check if file, fo, generator, iterator. | 380 # Check if file, fo, generator, iterator. |
| 349 # If not, run through normal process. | 381 # If not, run through normal process. |
| 350 | 382 |
| 351 # Nottin' on you. | 383 # Nottin' on you. |
| 352 body = None | 384 body = None |
| 353 content_type = None | 385 content_type = None |
| 354 length = None | 386 length = None |
| 355 is_stream = False | |
| 356 | 387 |
| 357 is_stream = all([ | 388 is_stream = all([ |
| 358 hasattr(data, '__iter__'), | 389 hasattr(data, '__iter__'), |
| 359 not isinstance(data, basestring), | 390 not isinstance(data, basestring), |
| 360 not isinstance(data, list), | 391 not isinstance(data, list), |
| 361 not isinstance(data, dict) | 392 not isinstance(data, dict) |
| 362 ]) | 393 ]) |
| 363 | 394 |
| 364 try: | 395 try: |
| 365 length = super_len(data) | 396 length = super_len(data) |
| 366 except (TypeError, AttributeError): | 397 except (TypeError, AttributeError, UnsupportedOperation): |
| 367 length = False | 398 length = None |
| 368 | 399 |
| 369 if is_stream: | 400 if is_stream: |
| 370 body = data | 401 body = data |
| 371 | 402 |
| 372 if files: | 403 if files: |
| 373 raise NotImplementedError('Streamed bodies and files are mutuall
y exclusive.') | 404 raise NotImplementedError('Streamed bodies and files are mutuall
y exclusive.') |
| 374 | 405 |
| 375 if length: | 406 if length is not None: |
| 376 self.headers['Content-Length'] = str(length) | 407 self.headers['Content-Length'] = str(length) |
| 377 else: | 408 else: |
| 378 self.headers['Transfer-Encoding'] = 'chunked' | 409 self.headers['Transfer-Encoding'] = 'chunked' |
| 379 # Check if file, fo, generator, iterator. | |
| 380 # If not, run through normal process. | |
| 381 | |
| 382 else: | 410 else: |
| 383 # Multi-part file uploads. | 411 # Multi-part file uploads. |
| 384 if files: | 412 if files: |
| 385 (body, content_type) = self._encode_files(files, data) | 413 (body, content_type) = self._encode_files(files, data) |
| 386 else: | 414 else: |
| 387 if data: | 415 if data: |
| 388 body = self._encode_params(data) | 416 body = self._encode_params(data) |
| 389 if isinstance(data, str) or isinstance(data, builtin_str) or
hasattr(data, 'read'): | 417 if isinstance(data, str) or isinstance(data, builtin_str) or
hasattr(data, 'read'): |
| 390 content_type = None | 418 content_type = None |
| 391 else: | 419 else: |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 request, this avoids reading the content at once into memory for | 558 request, this avoids reading the content at once into memory for |
| 531 large responses. The chunk size is the number of bytes it should | 559 large responses. The chunk size is the number of bytes it should |
| 532 read into memory. This is not necessarily the length of each item | 560 read into memory. This is not necessarily the length of each item |
| 533 returned as decoding can take place. | 561 returned as decoding can take place. |
| 534 """ | 562 """ |
| 535 if self._content_consumed: | 563 if self._content_consumed: |
| 536 # simulate reading small chunks of the content | 564 # simulate reading small chunks of the content |
| 537 return iter_slices(self._content, chunk_size) | 565 return iter_slices(self._content, chunk_size) |
| 538 | 566 |
| 539 def generate(): | 567 def generate(): |
| 540 while 1: | 568 try: |
| 541 chunk = self.raw.read(chunk_size, decode_content=True) | 569 # Special case for urllib3. |
| 542 if not chunk: | 570 try: |
| 543 break | 571 for chunk in self.raw.stream(chunk_size, |
| 544 yield chunk | 572 decode_content=True): |
| 573 yield chunk |
| 574 except IncompleteRead as e: |
| 575 raise ChunkedEncodingError(e) |
| 576 except AttributeError: |
| 577 # Standard file-like object. |
| 578 while 1: |
| 579 chunk = self.raw.read(chunk_size) |
| 580 if not chunk: |
| 581 break |
| 582 yield chunk |
| 583 |
| 545 self._content_consumed = True | 584 self._content_consumed = True |
| 546 | 585 |
| 547 gen = generate() | 586 gen = generate() |
| 548 | 587 |
| 549 if decode_unicode: | 588 if decode_unicode: |
| 550 gen = stream_decode_response_unicode(gen, self) | 589 gen = stream_decode_response_unicode(gen, self) |
| 551 | 590 |
| 552 return gen | 591 return gen |
| 553 | 592 |
| 554 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None): | 593 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None): |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 if 400 <= self.status_code < 500: | 715 if 400 <= self.status_code < 500: |
| 677 http_error_msg = '%s Client Error: %s' % (self.status_code, self.rea
son) | 716 http_error_msg = '%s Client Error: %s' % (self.status_code, self.rea
son) |
| 678 | 717 |
| 679 elif 500 <= self.status_code < 600: | 718 elif 500 <= self.status_code < 600: |
| 680 http_error_msg = '%s Server Error: %s' % (self.status_code, self.rea
son) | 719 http_error_msg = '%s Server Error: %s' % (self.status_code, self.rea
son) |
| 681 | 720 |
| 682 if http_error_msg: | 721 if http_error_msg: |
| 683 raise HTTPError(http_error_msg, response=self) | 722 raise HTTPError(http_error_msg, response=self) |
| 684 | 723 |
| 685 def close(self): | 724 def close(self): |
| 725 """Closes the underlying file descriptor and releases the connection |
| 726 back to the pool. |
| 727 |
| 728 *Note: Should not normally need to be called explicitly.* |
| 729 """ |
| 686 return self.raw.release_conn() | 730 return self.raw.release_conn() |
| OLD | NEW |