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 |