comparison venv/lib/python2.7/site-packages/requests/models.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
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 datetime
12
13 from io import BytesIO, UnsupportedOperation
14 from .hooks import default_hooks
15 from .structures import CaseInsensitiveDict
16
17 from .auth import HTTPBasicAuth
18 from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar
19 from .packages.urllib3.fields import RequestField
20 from .packages.urllib3.filepost import encode_multipart_formdata
21 from .packages.urllib3.util import parse_url
22 from .packages.urllib3.exceptions import (
23 DecodeError, ReadTimeoutError, ProtocolError, LocationParseError)
24 from .exceptions import (
25 HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError,
26 ContentDecodingError, ConnectionError, StreamConsumedError)
27 from .utils import (
28 guess_filename, get_auth_from_url, requote_uri,
29 stream_decode_response_unicode, to_key_val_list, parse_header_links,
30 iter_slices, guess_json_utf, super_len, to_native_string)
31 from .compat import (
32 cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO,
33 is_py2, chardet, builtin_str, basestring)
34 from .compat import json as complexjson
35 from .status_codes import codes
36
37 #: The set of HTTP status codes that indicate an automatically
38 #: processable redirect.
39 REDIRECT_STATI = (
40 codes.moved, # 301
41 codes.found, # 302
42 codes.other, # 303
43 codes.temporary_redirect, # 307
44 codes.permanent_redirect, # 308
45 )
46
47 DEFAULT_REDIRECT_LIMIT = 30
48 CONTENT_CHUNK_SIZE = 10 * 1024
49 ITER_CHUNK_SIZE = 512
50
51
52 class RequestEncodingMixin(object):
53 @property
54 def path_url(self):
55 """Build the path URL to use."""
56
57 url = []
58
59 p = urlsplit(self.url)
60
61 path = p.path
62 if not path:
63 path = '/'
64
65 url.append(path)
66
67 query = p.query
68 if query:
69 url.append('?')
70 url.append(query)
71
72 return ''.join(url)
73
74 @staticmethod
75 def _encode_params(data):
76 """Encode parameters in a piece of data.
77
78 Will successfully encode parameters when passed as a dict or a list of
79 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
80 if parameters are supplied as a dict.
81 """
82
83 if isinstance(data, (str, bytes)):
84 return data
85 elif hasattr(data, 'read'):
86 return data
87 elif hasattr(data, '__iter__'):
88 result = []
89 for k, vs in to_key_val_list(data):
90 if isinstance(vs, basestring) or not hasattr(vs, '__iter__'):
91 vs = [vs]
92 for v in vs:
93 if v is not None:
94 result.append(
95 (k.encode('utf-8') if isinstance(k, str) else k,
96 v.encode('utf-8') if isinstance(v, str) else v))
97 return urlencode(result, doseq=True)
98 else:
99 return data
100
101 @staticmethod
102 def _encode_files(files, data):
103 """Build the body for a multipart/form-data request.
104
105 Will successfully encode files when passed as a dict or a list of
106 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
107 if parameters are supplied as a dict.
108
109 """
110 if (not files):
111 raise ValueError("Files must be provided.")
112 elif isinstance(data, basestring):
113 raise ValueError("Data must not be a string.")
114
115 new_fields = []
116 fields = to_key_val_list(data or {})
117 files = to_key_val_list(files or {})
118
119 for field, val in fields:
120 if isinstance(val, basestring) or not hasattr(val, '__iter__'):
121 val = [val]
122 for v in val:
123 if v is not None:
124 # Don't call str() on bytestrings: in Py3 it all goes wrong.
125 if not isinstance(v, bytes):
126 v = str(v)
127
128 new_fields.append(
129 (field.decode('utf-8') if isinstance(field, bytes) else field,
130 v.encode('utf-8') if isinstance(v, str) else v))
131
132 for (k, v) in files:
133 # support for explicit filename
134 ft = None
135 fh = None
136 if isinstance(v, (tuple, list)):
137 if len(v) == 2:
138 fn, fp = v
139 elif len(v) == 3:
140 fn, fp, ft = v
141 else:
142 fn, fp, ft, fh = v
143 else:
144 fn = guess_filename(v) or k
145 fp = v
146
147 if isinstance(fp, (str, bytes, bytearray)):
148 fdata = fp
149 else:
150 fdata = fp.read()
151
152 rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
153 rf.make_multipart(content_type=ft)
154 new_fields.append(rf)
155
156 body, content_type = encode_multipart_formdata(new_fields)
157
158 return body, content_type
159
160
161 class RequestHooksMixin(object):
162 def register_hook(self, event, hook):
163 """Properly register a hook."""
164
165 if event not in self.hooks:
166 raise ValueError('Unsupported event specified, with event name "%s"' % (event))
167
168 if isinstance(hook, collections.Callable):
169 self.hooks[event].append(hook)
170 elif hasattr(hook, '__iter__'):
171 self.hooks[event].extend(h for h in hook if isinstance(h, collections.Callable))
172
173 def deregister_hook(self, event, hook):
174 """Deregister a previously registered hook.
175 Returns True if the hook existed, False if not.
176 """
177
178 try:
179 self.hooks[event].remove(hook)
180 return True
181 except ValueError:
182 return False
183
184
185 class Request(RequestHooksMixin):
186 """A user-created :class:`Request <Request>` object.
187
188 Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.
189
190 :param method: HTTP method to use.
191 :param url: URL to send.
192 :param headers: dictionary of headers to send.
193 :param files: dictionary of {filename: fileobject} files to multipart upload.
194 :param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place.
195 :param json: json for the body to attach to the request (if files or data is not specified).
196 :param params: dictionary of URL parameters to append to the URL.
197 :param auth: Auth handler or (user, pass) tuple.
198 :param cookies: dictionary or CookieJar of cookies to attach to this request.
199 :param hooks: dictionary of callback hooks, for internal usage.
200
201 Usage::
202
203 >>> import requests
204 >>> req = requests.Request('GET', 'http://httpbin.org/get')
205 >>> req.prepare()
206 <PreparedRequest [GET]>
207
208 """
209 def __init__(self, method=None, url=None, headers=None, files=None,
210 data=None, params=None, auth=None, cookies=None, hooks=None, json=None):
211
212 # Default empty dicts for dict params.
213 data = [] if data is None else data
214 files = [] if files is None else files
215 headers = {} if headers is None else headers
216 params = {} if params is None else params
217 hooks = {} if hooks is None else hooks
218
219 self.hooks = default_hooks()
220 for (k, v) in list(hooks.items()):
221 self.register_hook(event=k, hook=v)
222
223 self.method = method
224 self.url = url
225 self.headers = headers
226 self.files = files
227 self.data = data
228 self.json = json
229 self.params = params
230 self.auth = auth
231 self.cookies = cookies
232
233 def __repr__(self):
234 return '<Request [%s]>' % (self.method)
235
236 def prepare(self):
237 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it."""
238 p = PreparedRequest()
239 p.prepare(
240 method=self.method,
241 url=self.url,
242 headers=self.headers,
243 files=self.files,
244 data=self.data,
245 json=self.json,
246 params=self.params,
247 auth=self.auth,
248 cookies=self.cookies,
249 hooks=self.hooks,
250 )
251 return p
252
253
254 class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
255 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
256 containing the exact bytes that will be sent to the server.
257
258 Generated from either a :class:`Request <Request>` object or manually.
259
260 Usage::
261
262 >>> import requests
263 >>> req = requests.Request('GET', 'http://httpbin.org/get')
264 >>> r = req.prepare()
265 <PreparedRequest [GET]>
266
267 >>> s = requests.Session()
268 >>> s.send(r)
269 <Response [200]>
270
271 """
272
273 def __init__(self):
274 #: HTTP verb to send to the server.
275 self.method = None
276 #: HTTP URL to send the request to.
277 self.url = None
278 #: dictionary of HTTP headers.
279 self.headers = None
280 # The `CookieJar` used to create the Cookie header will be stored here
281 # after prepare_cookies is called
282 self._cookies = None
283 #: request body to send to the server.
284 self.body = None
285 #: dictionary of callback hooks, for internal usage.
286 self.hooks = default_hooks()
287
288 def prepare(self, method=None, url=None, headers=None, files=None,
289 data=None, params=None, auth=None, cookies=None, hooks=None, json=None):
290 """Prepares the entire request with the given parameters."""
291
292 self.prepare_method(method)
293 self.prepare_url(url, params)
294 self.prepare_headers(headers)
295 self.prepare_cookies(cookies)
296 self.prepare_body(data, files, json)
297 self.prepare_auth(auth, url)
298
299 # Note that prepare_auth must be last to enable authentication schemes
300 # such as OAuth to work on a fully prepared request.
301
302 # This MUST go after prepare_auth. Authenticators could add a hook
303 self.prepare_hooks(hooks)
304
305 def __repr__(self):
306 return '<PreparedRequest [%s]>' % (self.method)
307
308 def copy(self):
309 p = PreparedRequest()
310 p.method = self.method
311 p.url = self.url
312 p.headers = self.headers.copy() if self.headers is not None else None
313 p._cookies = _copy_cookie_jar(self._cookies)
314 p.body = self.body
315 p.hooks = self.hooks
316 return p
317
318 def prepare_method(self, method):
319 """Prepares the given HTTP method."""
320 self.method = method
321 if self.method is not None:
322 self.method = self.method.upper()
323
324 def prepare_url(self, url, params):
325 """Prepares the given HTTP URL."""
326 #: Accept objects that have string representations.
327 #: We're unable to blindy call unicode/str functions
328 #: as this will include the bytestring indicator (b'')
329 #: on python 3.x.
330 #: https://github.com/kennethreitz/requests/pull/2238
331 if isinstance(url, bytes):
332 url = url.decode('utf8')
333 else:
334 url = unicode(url) if is_py2 else str(url)
335
336 # Don't do any URL preparation for non-HTTP schemes like `mailto`,
337 # `data` etc to work around exceptions from `url_parse`, which
338 # handles RFC 3986 only.
339 if ':' in url and not url.lower().startswith('http'):
340 self.url = url
341 return
342
343 # Support for unicode domain names and paths.
344 try:
345 scheme, auth, host, port, path, query, fragment = parse_url(url)
346 except LocationParseError as e:
347 raise InvalidURL(*e.args)
348
349 if not scheme:
350 error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?")
351 error = error.format(to_native_string(url, 'utf8'))
352
353 raise MissingSchema(error)
354
355 if not host:
356 raise InvalidURL("Invalid URL %r: No host supplied" % url)
357
358 # Only want to apply IDNA to the hostname
359 try:
360 host = host.encode('idna').decode('utf-8')
361 except UnicodeError:
362 raise InvalidURL('URL has an invalid label.')
363
364 # Carefully reconstruct the network location
365 netloc = auth or ''
366 if netloc:
367 netloc += '@'
368 netloc += host
369 if port:
370 netloc += ':' + str(port)
371
372 # Bare domains aren't valid URLs.
373 if not path:
374 path = '/'
375
376 if is_py2:
377 if isinstance(scheme, str):
378 scheme = scheme.encode('utf-8')
379 if isinstance(netloc, str):
380 netloc = netloc.encode('utf-8')
381 if isinstance(path, str):
382 path = path.encode('utf-8')
383 if isinstance(query, str):
384 query = query.encode('utf-8')
385 if isinstance(fragment, str):
386 fragment = fragment.encode('utf-8')
387
388 enc_params = self._encode_params(params)
389 if enc_params:
390 if query:
391 query = '%s&%s' % (query, enc_params)
392 else:
393 query = enc_params
394
395 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
396 self.url = url
397
398 def prepare_headers(self, headers):
399 """Prepares the given HTTP headers."""
400
401 if headers:
402 self.headers = CaseInsensitiveDict((to_native_string(name), value) for name, value in headers.items())
403 else:
404 self.headers = CaseInsensitiveDict()
405
406 def prepare_body(self, data, files, json=None):
407 """Prepares the given HTTP body data."""
408
409 # Check if file, fo, generator, iterator.
410 # If not, run through normal process.
411
412 # Nottin' on you.
413 body = None
414 content_type = None
415 length = None
416
417 if data == {} and json is not None:
418 content_type = 'application/json'
419 body = complexjson.dumps(json)
420
421 is_stream = all([
422 hasattr(data, '__iter__'),
423 not isinstance(data, (basestring, list, tuple, dict))
424 ])
425
426 try:
427 length = super_len(data)
428 except (TypeError, AttributeError, UnsupportedOperation):
429 length = None
430
431 if is_stream:
432 body = data
433
434 if files:
435 raise NotImplementedError('Streamed bodies and files are mutually exclusive.')
436
437 if length is not None:
438 self.headers['Content-Length'] = builtin_str(length)
439 else:
440 self.headers['Transfer-Encoding'] = 'chunked'
441 else:
442 # Multi-part file uploads.
443 if files:
444 (body, content_type) = self._encode_files(files, data)
445 else:
446 if data:
447 body = self._encode_params(data)
448 if isinstance(data, basestring) or hasattr(data, 'read'):
449 content_type = None
450 else:
451 content_type = 'application/x-www-form-urlencoded'
452
453 self.prepare_content_length(body)
454
455 # Add content-type if it wasn't explicitly provided.
456 if content_type and ('content-type' not in self.headers):
457 self.headers['Content-Type'] = content_type
458
459 self.body = body
460
461 def prepare_content_length(self, body):
462 if hasattr(body, 'seek') and hasattr(body, 'tell'):
463 body.seek(0, 2)
464 self.headers['Content-Length'] = builtin_str(body.tell())
465 body.seek(0, 0)
466 elif body is not None:
467 l = super_len(body)
468 if l:
469 self.headers['Content-Length'] = builtin_str(l)
470 elif (self.method not in ('GET', 'HEAD')) and (self.headers.get('Content-Length') is None):
471 self.headers['Content-Length'] = '0'
472
473 def prepare_auth(self, auth, url=''):
474 """Prepares the given HTTP auth data."""
475
476 # If no Auth is explicitly provided, extract it from the URL first.
477 if auth is None:
478 url_auth = get_auth_from_url(self.url)
479 auth = url_auth if any(url_auth) else None
480
481 if auth:
482 if isinstance(auth, tuple) and len(auth) == 2:
483 # special-case basic HTTP auth
484 auth = HTTPBasicAuth(*auth)
485
486 # Allow auth to make its changes.
487 r = auth(self)
488
489 # Update self to reflect the auth changes.
490 self.__dict__.update(r.__dict__)
491
492 # Recompute Content-Length
493 self.prepare_content_length(self.body)
494
495 def prepare_cookies(self, cookies):
496 """Prepares the given HTTP cookie data.
497
498 This function eventually generates a ``Cookie`` header from the
499 given cookies using cookielib. Due to cookielib's design, the header
500 will not be regenerated if it already exists, meaning this function
501 can only be called once for the life of the
502 :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls
503 to ``prepare_cookies`` will have no actual effect, unless the "Cookie"
504 header is removed beforehand."""
505
506 if isinstance(cookies, cookielib.CookieJar):
507 self._cookies = cookies
508 else:
509 self._cookies = cookiejar_from_dict(cookies)
510
511 cookie_header = get_cookie_header(self._cookies, self)
512 if cookie_header is not None:
513 self.headers['Cookie'] = cookie_header
514
515 def prepare_hooks(self, hooks):
516 """Prepares the given hooks."""
517 # hooks can be passed as None to the prepare method and to this
518 # method. To prevent iterating over None, simply use an empty list
519 # if hooks is False-y
520 hooks = hooks or []
521 for event in hooks:
522 self.register_hook(event, hooks[event])
523
524
525 class Response(object):
526 """The :class:`Response <Response>` object, which contains a
527 server's response to an HTTP request.
528 """
529
530 __attrs__ = [
531 '_content', 'status_code', 'headers', 'url', 'history',
532 'encoding', 'reason', 'cookies', 'elapsed', 'request'
533 ]
534
535 def __init__(self):
536 super(Response, self).__init__()
537
538 self._content = False
539 self._content_consumed = False
540
541 #: Integer Code of responded HTTP Status, e.g. 404 or 200.
542 self.status_code = None
543
544 #: Case-insensitive Dictionary of Response Headers.
545 #: For example, ``headers['content-encoding']`` will return the
546 #: value of a ``'Content-Encoding'`` response header.
547 self.headers = CaseInsensitiveDict()
548
549 #: File-like object representation of response (for advanced usage).
550 #: Use of ``raw`` requires that ``stream=True`` be set on the request.
551 # This requirement does not apply for use internally to Requests.
552 self.raw = None
553
554 #: Final URL location of Response.
555 self.url = None
556
557 #: Encoding to decode with when accessing r.text.
558 self.encoding = None
559
560 #: A list of :class:`Response <Response>` objects from
561 #: the history of the Request. Any redirect responses will end
562 #: up here. The list is sorted from the oldest to the most recent request.
563 self.history = []
564
565 #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
566 self.reason = None
567
568 #: A CookieJar of Cookies the server sent back.
569 self.cookies = cookiejar_from_dict({})
570
571 #: The amount of time elapsed between sending the request
572 #: and the arrival of the response (as a timedelta).
573 #: This property specifically measures the time taken between sending
574 #: the first byte of the request and finishing parsing the headers. It
575 #: is therefore unaffected by consuming the response content or the
576 #: value of the ``stream`` keyword argument.
577 self.elapsed = datetime.timedelta(0)
578
579 #: The :class:`PreparedRequest <PreparedRequest>` object to which this
580 #: is a response.
581 self.request = None
582
583 def __getstate__(self):
584 # Consume everything; accessing the content attribute makes
585 # sure the content has been fully read.
586 if not self._content_consumed:
587 self.content
588
589 return dict(
590 (attr, getattr(self, attr, None))
591 for attr in self.__attrs__
592 )
593
594 def __setstate__(self, state):
595 for name, value in state.items():
596 setattr(self, name, value)
597
598 # pickled objects do not have .raw
599 setattr(self, '_content_consumed', True)
600 setattr(self, 'raw', None)
601
602 def __repr__(self):
603 return '<Response [%s]>' % (self.status_code)
604
605 def __bool__(self):
606 """Returns true if :attr:`status_code` is 'OK'."""
607 return self.ok
608
609 def __nonzero__(self):
610 """Returns true if :attr:`status_code` is 'OK'."""
611 return self.ok
612
613 def __iter__(self):
614 """Allows you to use a response as an iterator."""
615 return self.iter_content(128)
616
617 @property
618 def ok(self):
619 try:
620 self.raise_for_status()
621 except HTTPError:
622 return False
623 return True
624
625 @property
626 def is_redirect(self):
627 """True if this Response is a well-formed HTTP redirect that could have
628 been processed automatically (by :meth:`Session.resolve_redirects`).
629 """
630 return ('location' in self.headers and self.status_code in REDIRECT_STATI)
631
632 @property
633 def is_permanent_redirect(self):
634 """True if this Response one of the permanant versions of redirect"""
635 return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect))
636
637 @property
638 def apparent_encoding(self):
639 """The apparent encoding, provided by the chardet library"""
640 return chardet.detect(self.content)['encoding']
641
642 def iter_content(self, chunk_size=1, decode_unicode=False):
643 """Iterates over the response data. When stream=True is set on the
644 request, this avoids reading the content at once into memory for
645 large responses. The chunk size is the number of bytes it should
646 read into memory. This is not necessarily the length of each item
647 returned as decoding can take place.
648
649 If decode_unicode is True, content will be decoded using the best
650 available encoding based on the response.
651 """
652
653 def generate():
654 # Special case for urllib3.
655 if hasattr(self.raw, 'stream'):
656 try:
657 for chunk in self.raw.stream(chunk_size, decode_content=True):
658 yield chunk
659 except ProtocolError as e:
660 raise ChunkedEncodingError(e)
661 except DecodeError as e:
662 raise ContentDecodingError(e)
663 except ReadTimeoutError as e:
664 raise ConnectionError(e)
665 else:
666 # Standard file-like object.
667 while True:
668 chunk = self.raw.read(chunk_size)
669 if not chunk:
670 break
671 yield chunk
672
673 self._content_consumed = True
674
675 if self._content_consumed and isinstance(self._content, bool):
676 raise StreamConsumedError()
677 # simulate reading small chunks of the content
678 reused_chunks = iter_slices(self._content, chunk_size)
679
680 stream_chunks = generate()
681
682 chunks = reused_chunks if self._content_consumed else stream_chunks
683
684 if decode_unicode:
685 chunks = stream_decode_response_unicode(chunks, self)
686
687 return chunks
688
689 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None, delimiter=None):
690 """Iterates over the response data, one line at a time. When
691 stream=True is set on the request, this avoids reading the
692 content at once into memory for large responses.
693
694 .. note:: This method is not reentrant safe.
695 """
696
697 pending = None
698
699 for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode):
700
701 if pending is not None:
702 chunk = pending + chunk
703
704 if delimiter:
705 lines = chunk.split(delimiter)
706 else:
707 lines = chunk.splitlines()
708
709 if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
710 pending = lines.pop()
711 else:
712 pending = None
713
714 for line in lines:
715 yield line
716
717 if pending is not None:
718 yield pending
719
720 @property
721 def content(self):
722 """Content of the response, in bytes."""
723
724 if self._content is False:
725 # Read the contents.
726 try:
727 if self._content_consumed:
728 raise RuntimeError(
729 'The content for this response was already consumed')
730
731 if self.status_code == 0:
732 self._content = None
733 else:
734 self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
735
736 except AttributeError:
737 self._content = None
738
739 self._content_consumed = True
740 # don't need to release the connection; that's been handled by urllib3
741 # since we exhausted the data.
742 return self._content
743
744 @property
745 def text(self):
746 """Content of the response, in unicode.
747
748 If Response.encoding is None, encoding will be guessed using
749 ``chardet``.
750
751 The encoding of the response content is determined based solely on HTTP
752 headers, following RFC 2616 to the letter. If you can take advantage of
753 non-HTTP knowledge to make a better guess at the encoding, you should
754 set ``r.encoding`` appropriately before accessing this property.
755 """
756
757 # Try charset from content-type
758 content = None
759 encoding = self.encoding
760
761 if not self.content:
762 return str('')
763
764 # Fallback to auto-detected encoding.
765 if self.encoding is None:
766 encoding = self.apparent_encoding
767
768 # Decode unicode from given encoding.
769 try:
770 content = str(self.content, encoding, errors='replace')
771 except (LookupError, TypeError):
772 # A LookupError is raised if the encoding was not found which could
773 # indicate a misspelling or similar mistake.
774 #
775 # A TypeError can be raised if encoding is None
776 #
777 # So we try blindly encoding.
778 content = str(self.content, errors='replace')
779
780 return content
781
782 def json(self, **kwargs):
783 """Returns the json-encoded content of a response, if any.
784
785 :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
786 """
787
788 if not self.encoding and len(self.content) > 3:
789 # No encoding set. JSON RFC 4627 section 3 states we should expect
790 # UTF-8, -16 or -32. Detect which one to use; If the detection or
791 # decoding fails, fall back to `self.text` (using chardet to make
792 # a best guess).
793 encoding = guess_json_utf(self.content)
794 if encoding is not None:
795 try:
796 return complexjson.loads(
797 self.content.decode(encoding), **kwargs
798 )
799 except UnicodeDecodeError:
800 # Wrong UTF codec detected; usually because it's not UTF-8
801 # but some other 8-bit codec. This is an RFC violation,
802 # and the server didn't bother to tell us what codec *was*
803 # used.
804 pass
805 return complexjson.loads(self.text, **kwargs)
806
807 @property
808 def links(self):
809 """Returns the parsed header links of the response, if any."""
810
811 header = self.headers.get('link')
812
813 # l = MultiDict()
814 l = {}
815
816 if header:
817 links = parse_header_links(header)
818
819 for link in links:
820 key = link.get('rel') or link.get('url')
821 l[key] = link
822
823 return l
824
825 def raise_for_status(self):
826 """Raises stored :class:`HTTPError`, if one occurred."""
827
828 http_error_msg = ''
829
830 if 400 <= self.status_code < 500:
831 http_error_msg = '%s Client Error: %s for url: %s' % (self.status_code, self.reason, self.url)
832
833 elif 500 <= self.status_code < 600:
834 http_error_msg = '%s Server Error: %s for url: %s' % (self.status_code, self.reason, self.url)
835
836 if http_error_msg:
837 raise HTTPError(http_error_msg, response=self)
838
839 def close(self):
840 """Releases the connection back to the pool. Once this method has been
841 called the underlying ``raw`` object must not be accessed again.
842
843 *Note: Should not normally need to be called explicitly.*
844 """
845 if not self._content_consumed:
846 return self.raw.close()
847
848 return self.raw.release_conn()