annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/requests/models.py @ 68:5028fdace37b

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