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