Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: swarm_client/third_party/requests/models.py

Issue 69143004: Delete swarm_client. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- coding: utf-8 -*-
2
3 """
4 requests.models
5 ~~~~~~~~~~~~~~~
6
7 This module contains the primary objects that power Requests.
8 """
9
10 import collections
11 import logging
12 import datetime
13
14 from io import BytesIO, UnsupportedOperation
15 from .hooks import default_hooks
16 from .structures import CaseInsensitiveDict
17
18 from .auth import HTTPBasicAuth
19 from .cookies import cookiejar_from_dict, get_cookie_header
20 from .packages.urllib3.filepost import encode_multipart_formdata
21 from .packages.urllib3.util import parse_url
22 from .exceptions import (
23 HTTPError, RequestException, MissingSchema, InvalidURL,
24 ChunkedEncodingError)
25 from .utils import (
26 guess_filename, get_auth_from_url, requote_uri,
27 stream_decode_response_unicode, to_key_val_list, parse_header_links,
28 iter_slices, guess_json_utf, super_len, to_native_string)
29 from .compat import (
30 cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO,
31 is_py2, chardet, json, builtin_str, basestring, IncompleteRead)
32
33 CONTENT_CHUNK_SIZE = 10 * 1024
34 ITER_CHUNK_SIZE = 512
35
36 log = logging.getLogger(__name__)
37
38
39 class RequestEncodingMixin(object):
40 @property
41 def path_url(self):
42 """Build the path URL to use."""
43
44 url = []
45
46 p = urlsplit(self.url)
47
48 path = p.path
49 if not path:
50 path = '/'
51
52 url.append(path)
53
54 query = p.query
55 if query:
56 url.append('?')
57 url.append(query)
58
59 return ''.join(url)
60
61 @staticmethod
62 def _encode_params(data):
63 """Encode parameters in a piece of data.
64
65 Will successfully encode parameters when passed as a dict or a list of
66 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
67 if parameters are supplied as a dict.
68 """
69
70 if isinstance(data, (str, bytes)):
71 return data
72 elif hasattr(data, 'read'):
73 return data
74 elif hasattr(data, '__iter__'):
75 result = []
76 for k, vs in to_key_val_list(data):
77 if isinstance(vs, basestring) or not hasattr(vs, '__iter__'):
78 vs = [vs]
79 for v in vs:
80 if v is not None:
81 result.append(
82 (k.encode('utf-8') if isinstance(k, str) else k,
83 v.encode('utf-8') if isinstance(v, str) else v))
84 return urlencode(result, doseq=True)
85 else:
86 return data
87
88 @staticmethod
89 def _encode_files(files, data):
90 """Build the body for a multipart/form-data request.
91
92 Will successfully encode files when passed as a dict or a list of
93 2-tuples. Order is retained if data is a list of 2-tuples but abritrary
94 if parameters are supplied as a dict.
95
96 """
97 if (not files):
98 raise ValueError("Files must be provided.")
99 elif isinstance(data, basestring):
100 raise ValueError("Data must not be a string.")
101
102 new_fields = []
103 fields = to_key_val_list(data or {})
104 files = to_key_val_list(files or {})
105
106 for field, val in fields:
107 if isinstance(val, basestring) or not hasattr(val, '__iter__'):
108 val = [val]
109 for v in val:
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
115 new_fields.append(
116 (field.decode('utf-8') if isinstance(field, bytes) else field,
117 v.encode('utf-8') if isinstance(v, str) else v))
118
119 for (k, v) in files:
120 # support for explicit filename
121 ft = None
122 if isinstance(v, (tuple, list)):
123 if len(v) == 2:
124 fn, fp = v
125 else:
126 fn, fp, ft = v
127 else:
128 fn = guess_filename(v) or k
129 fp = v
130 if isinstance(fp, str):
131 fp = StringIO(fp)
132 if isinstance(fp, bytes):
133 fp = BytesIO(fp)
134
135 if ft:
136 new_v = (fn, fp.read(), ft)
137 else:
138 new_v = (fn, fp.read())
139 new_fields.append((k, new_v))
140
141 body, content_type = encode_multipart_formdata(new_fields)
142
143 return body, content_type
144
145
146 class RequestHooksMixin(object):
147 def register_hook(self, event, hook):
148 """Properly register a hook."""
149
150 if event not in self.hooks:
151 raise ValueError('Unsupported event specified, with event name "%s"' % (event))
152
153 if isinstance(hook, collections.Callable):
154 self.hooks[event].append(hook)
155 elif hasattr(hook, '__iter__'):
156 self.hooks[event].extend(h for h in hook if isinstance(h, collection s.Callable))
157
158 def deregister_hook(self, event, hook):
159 """Deregister a previously registered hook.
160 Returns True if the hook existed, False if not.
161 """
162
163 try:
164 self.hooks[event].remove(hook)
165 return True
166 except ValueError:
167 return False
168
169
170 class Request(RequestHooksMixin):
171 """A user-created :class:`Request <Request>` object.
172
173 Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.
174
175 :param method: HTTP method to use.
176 :param url: URL to send.
177 :param headers: dictionary of headers to send.
178 :param files: dictionary of {filename: fileobject} files to multipart upload .
179 :param data: the body to attach the request. If a dictionary is provided, fo rm-encoding will take place.
180 :param params: dictionary of URL parameters to append to the URL.
181 :param auth: Auth handler or (user, pass) tuple.
182 :param cookies: dictionary or CookieJar of cookies to attach to this request .
183 :param hooks: dictionary of callback hooks, for internal usage.
184
185 Usage::
186
187 >>> import requests
188 >>> req = requests.Request('GET', 'http://httpbin.org/get')
189 >>> req.prepare()
190 <PreparedRequest [GET]>
191
192 """
193 def __init__(self,
194 method=None,
195 url=None,
196 headers=None,
197 files=None,
198 data=None,
199 params=None,
200 auth=None,
201 cookies=None,
202 hooks=None):
203
204 # Default empty dicts for dict params.
205 data = [] if data is None else data
206 files = [] if files is None else files
207 headers = {} if headers is None else headers
208 params = {} if params is None else params
209 hooks = {} if hooks is None else hooks
210
211 self.hooks = default_hooks()
212 for (k, v) in list(hooks.items()):
213 self.register_hook(event=k, hook=v)
214
215 self.method = method
216 self.url = url
217 self.headers = headers
218 self.files = files
219 self.data = data
220 self.params = params
221 self.auth = auth
222 self.cookies = cookies
223
224 def __repr__(self):
225 return '<Request [%s]>' % (self.method)
226
227 def prepare(self):
228 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmiss ion and returns it."""
229 p = PreparedRequest()
230 p.prepare(
231 method=self.method,
232 url=self.url,
233 headers=self.headers,
234 files=self.files,
235 data=self.data,
236 params=self.params,
237 auth=self.auth,
238 cookies=self.cookies,
239 hooks=self.hooks,
240 )
241 return p
242
243
244 class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
245 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
246 containing the exact bytes that will be sent to the server.
247
248 Generated from either a :class:`Request <Request>` object or manually.
249
250 Usage::
251
252 >>> import requests
253 >>> req = requests.Request('GET', 'http://httpbin.org/get')
254 >>> r = req.prepare()
255 <PreparedRequest [GET]>
256
257 >>> s = requests.Session()
258 >>> s.send(r)
259 <Response [200]>
260
261 """
262
263 def __init__(self):
264 #: HTTP verb to send to the server.
265 self.method = None
266 #: HTTP URL to send the request to.
267 self.url = None
268 #: dictionary of HTTP headers.
269 self.headers = None
270 #: request body to send to the server.
271 self.body = None
272 #: dictionary of callback hooks, for internal usage.
273 self.hooks = default_hooks()
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
291 def __repr__(self):
292 return '<PreparedRequest [%s]>' % (self.method)
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
303 def prepare_method(self, method):
304 """Prepares the given HTTP method."""
305 self.method = method
306 if self.method is not None:
307 self.method = self.method.upper()
308
309 def prepare_url(self, url, params):
310 """Prepares the given HTTP URL."""
311 #: Accept objects that have string representations.
312 try:
313 url = unicode(url)
314 except NameError:
315 # We're on Python 3.
316 url = str(url)
317 except UnicodeDecodeError:
318 pass
319
320 # Support for unicode domain names and paths.
321 scheme, auth, host, port, path, query, fragment = parse_url(url)
322
323 if not scheme:
324 raise MissingSchema("Invalid URL %r: No schema supplied" % url)
325
326 if not host:
327 raise InvalidURL("Invalid URL %r: No host supplied" % url)
328
329 # Only want to apply IDNA to the hostname
330 try:
331 host = host.encode('idna').decode('utf-8')
332 except UnicodeError:
333 raise InvalidURL('URL has an invalid label.')
334
335 # Carefully reconstruct the network location
336 netloc = auth or ''
337 if netloc:
338 netloc += '@'
339 netloc += host
340 if port:
341 netloc += ':' + str(port)
342
343 # Bare domains aren't valid URLs.
344 if not path:
345 path = '/'
346
347 if is_py2:
348 if isinstance(scheme, str):
349 scheme = scheme.encode('utf-8')
350 if isinstance(netloc, str):
351 netloc = netloc.encode('utf-8')
352 if isinstance(path, str):
353 path = path.encode('utf-8')
354 if isinstance(query, str):
355 query = query.encode('utf-8')
356 if isinstance(fragment, str):
357 fragment = fragment.encode('utf-8')
358
359 enc_params = self._encode_params(params)
360 if enc_params:
361 if query:
362 query = '%s&%s' % (query, enc_params)
363 else:
364 query = enc_params
365
366 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragmen t]))
367 self.url = url
368
369 def prepare_headers(self, headers):
370 """Prepares the given HTTP headers."""
371
372 if headers:
373 self.headers = CaseInsensitiveDict((to_native_string(name), value) f or name, value in headers.items())
374 else:
375 self.headers = CaseInsensitiveDict()
376
377 def prepare_body(self, data, files):
378 """Prepares the given HTTP body data."""
379
380 # Check if file, fo, generator, iterator.
381 # If not, run through normal process.
382
383 # Nottin' on you.
384 body = None
385 content_type = None
386 length = None
387
388 is_stream = all([
389 hasattr(data, '__iter__'),
390 not isinstance(data, basestring),
391 not isinstance(data, list),
392 not isinstance(data, dict)
393 ])
394
395 try:
396 length = super_len(data)
397 except (TypeError, AttributeError, UnsupportedOperation):
398 length = None
399
400 if is_stream:
401 body = data
402
403 if files:
404 raise NotImplementedError('Streamed bodies and files are mutuall y exclusive.')
405
406 if length is not None:
407 self.headers['Content-Length'] = str(length)
408 else:
409 self.headers['Transfer-Encoding'] = 'chunked'
410 else:
411 # Multi-part file uploads.
412 if files:
413 (body, content_type) = self._encode_files(files, data)
414 else:
415 if data:
416 body = self._encode_params(data)
417 if isinstance(data, str) or isinstance(data, builtin_str) or hasattr(data, 'read'):
418 content_type = None
419 else:
420 content_type = 'application/x-www-form-urlencoded'
421
422 self.prepare_content_length(body)
423
424 # Add content-type if it wasn't explicitly provided.
425 if (content_type) and (not 'content-type' in self.headers):
426 self.headers['Content-Type'] = content_type
427
428 self.body = body
429
430 def prepare_content_length(self, body):
431 if hasattr(body, 'seek') and hasattr(body, 'tell'):
432 body.seek(0, 2)
433 self.headers['Content-Length'] = str(body.tell())
434 body.seek(0, 0)
435 elif body is not None:
436 l = super_len(body)
437 if l:
438 self.headers['Content-Length'] = str(l)
439 elif self.method not in ('GET', 'HEAD'):
440 self.headers['Content-Length'] = '0'
441
442 def prepare_auth(self, auth, url=''):
443 """Prepares the given HTTP auth data."""
444
445 # If no Auth is explicitly provided, extract it from the URL first.
446 if auth is None:
447 url_auth = get_auth_from_url(self.url)
448 auth = url_auth if any(url_auth) else None
449
450 if auth:
451 if isinstance(auth, tuple) and len(auth) == 2:
452 # special-case basic HTTP auth
453 auth = HTTPBasicAuth(*auth)
454
455 # Allow auth to make its changes.
456 r = auth(self)
457
458 # Update self to reflect the auth changes.
459 self.__dict__.update(r.__dict__)
460
461 # Recompute Content-Length
462 self.prepare_content_length(self.body)
463
464 def prepare_cookies(self, cookies):
465 """Prepares the given HTTP cookie data."""
466
467 if isinstance(cookies, cookielib.CookieJar):
468 cookies = cookies
469 else:
470 cookies = cookiejar_from_dict(cookies)
471
472 if 'cookie' not in self.headers:
473 cookie_header = get_cookie_header(cookies, self)
474 if cookie_header is not None:
475 self.headers['Cookie'] = cookie_header
476
477 def prepare_hooks(self, hooks):
478 """Prepares the given hooks."""
479 for event in hooks:
480 self.register_hook(event, hooks[event])
481
482
483 class Response(object):
484 """The :class:`Response <Response>` object, which contains a
485 server's response to an HTTP request.
486 """
487
488 def __init__(self):
489 super(Response, self).__init__()
490
491 self._content = False
492 self._content_consumed = False
493
494 #: Integer Code of responded HTTP Status.
495 self.status_code = None
496
497 #: Case-insensitive Dictionary of Response Headers.
498 #: For example, ``headers['content-encoding']`` will return the
499 #: value of a ``'Content-Encoding'`` response header.
500 self.headers = CaseInsensitiveDict()
501
502 #: File-like object representation of response (for advanced usage).
503 #: Requires that ``stream=True` on the request.
504 # This requirement does not apply for use internally to Requests.
505 self.raw = None
506
507 #: Final URL location of Response.
508 self.url = None
509
510 #: Encoding to decode with when accessing r.text.
511 self.encoding = None
512
513 #: A list of :class:`Response <Response>` objects from
514 #: the history of the Request. Any redirect responses will end
515 #: up here. The list is sorted from the oldest to the most recent reques t.
516 self.history = []
517
518 self.reason = None
519
520 #: A CookieJar of Cookies the server sent back.
521 self.cookies = cookiejar_from_dict({})
522
523 #: The amount of time elapsed between sending the request
524 #: and the arrival of the response (as a timedelta)
525 self.elapsed = datetime.timedelta(0)
526
527 def __repr__(self):
528 return '<Response [%s]>' % (self.status_code)
529
530 def __bool__(self):
531 """Returns true if :attr:`status_code` is 'OK'."""
532 return self.ok
533
534 def __nonzero__(self):
535 """Returns true if :attr:`status_code` is 'OK'."""
536 return self.ok
537
538 def __iter__(self):
539 """Allows you to use a response as an iterator."""
540 return self.iter_content(128)
541
542 @property
543 def ok(self):
544 try:
545 self.raise_for_status()
546 except RequestException:
547 return False
548 return True
549
550 @property
551 def apparent_encoding(self):
552 """The apparent encoding, provided by the lovely Charade library
553 (Thanks, Ian!)."""
554 return chardet.detect(self.content)['encoding']
555
556 def iter_content(self, chunk_size=1, decode_unicode=False):
557 """Iterates over the response data. When stream=True is set on the
558 request, this avoids reading the content at once into memory for
559 large responses. The chunk size is the number of bytes it should
560 read into memory. This is not necessarily the length of each item
561 returned as decoding can take place.
562 """
563 if self._content_consumed:
564 # simulate reading small chunks of the content
565 return iter_slices(self._content, chunk_size)
566
567 def generate():
568 try:
569 # Special case for urllib3.
570 try:
571 for chunk in self.raw.stream(chunk_size,
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
584 self._content_consumed = True
585
586 gen = generate()
587
588 if decode_unicode:
589 gen = stream_decode_response_unicode(gen, self)
590
591 return gen
592
593 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None):
594 """Iterates over the response data, one line at a time. When
595 stream=True is set on the request, this avoids reading the
596 content at once into memory for large responses.
597 """
598
599 pending = None
600
601 for chunk in self.iter_content(chunk_size=chunk_size,
602 decode_unicode=decode_unicode):
603
604 if pending is not None:
605 chunk = pending + chunk
606 lines = chunk.splitlines()
607
608 if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
609 pending = lines.pop()
610 else:
611 pending = None
612
613 for line in lines:
614 yield line
615
616 if pending is not None:
617 yield pending
618
619 @property
620 def content(self):
621 """Content of the response, in bytes."""
622
623 if self._content is False:
624 # Read the contents.
625 try:
626 if self._content_consumed:
627 raise RuntimeError(
628 'The content for this response was already consumed')
629
630 if self.status_code == 0:
631 self._content = None
632 else:
633 self._content = bytes().join(self.iter_content(CONTENT_CHUNK _SIZE)) or bytes()
634
635 except AttributeError:
636 self._content = None
637
638 self._content_consumed = True
639 # don't need to release the connection; that's been handled by urllib3
640 # since we exhausted the data.
641 return self._content
642
643 @property
644 def text(self):
645 """Content of the response, in unicode.
646
647 if Response.encoding is None and chardet module is available, encoding
648 will be guessed.
649 """
650
651 # Try charset from content-type
652 content = None
653 encoding = self.encoding
654
655 if not self.content:
656 return str('')
657
658 # Fallback to auto-detected encoding.
659 if self.encoding is None:
660 encoding = self.apparent_encoding
661
662 # Decode unicode from given encoding.
663 try:
664 content = str(self.content, encoding, errors='replace')
665 except (LookupError, TypeError):
666 # A LookupError is raised if the encoding was not found which could
667 # indicate a misspelling or similar mistake.
668 #
669 # A TypeError can be raised if encoding is None
670 #
671 # So we try blindly encoding.
672 content = str(self.content, errors='replace')
673
674 return content
675
676 def json(self, **kwargs):
677 """Returns the json-encoded content of a response, if any.
678
679 :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
680 """
681
682 if not self.encoding and len(self.content) > 3:
683 # No encoding set. JSON RFC 4627 section 3 states we should expect
684 # UTF-8, -16 or -32. Detect which one to use; If the detection or
685 # decoding fails, fall back to `self.text` (using chardet to make
686 # a best guess).
687 encoding = guess_json_utf(self.content)
688 if encoding is not None:
689 return json.loads(self.content.decode(encoding), **kwargs)
690 return json.loads(self.text or self.content, **kwargs)
691
692 @property
693 def links(self):
694 """Returns the parsed header links of the response, if any."""
695
696 header = self.headers.get('link')
697
698 # l = MultiDict()
699 l = {}
700
701 if header:
702 links = parse_header_links(header)
703
704 for link in links:
705 key = link.get('rel') or link.get('url')
706 l[key] = link
707
708 return l
709
710 def raise_for_status(self):
711 """Raises stored :class:`HTTPError`, if one occurred."""
712
713 http_error_msg = ''
714
715 if 400 <= self.status_code < 500:
716 http_error_msg = '%s Client Error: %s' % (self.status_code, self.rea son)
717
718 elif 500 <= self.status_code < 600:
719 http_error_msg = '%s Server Error: %s' % (self.status_code, self.rea son)
720
721 if http_error_msg:
722 raise HTTPError(http_error_msg, response=self)
723
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 """
730 return self.raw.release_conn()
OLDNEW
« no previous file with comments | « swarm_client/third_party/requests/hooks.py ('k') | swarm_client/third_party/requests/packages/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698