Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/requests/sessions.py: 55%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

269 statements  

1""" 

2requests.sessions 

3~~~~~~~~~~~~~~~~~ 

4 

5This module provides a Session object to manage and persist settings across 

6requests (cookies, auth, proxies). 

7""" 

8 

9import os 

10import sys 

11import time 

12from collections import OrderedDict 

13from datetime import timedelta 

14 

15from ._internal_utils import to_native_string 

16from .adapters import HTTPAdapter 

17from .auth import _basic_auth_str 

18from .compat import Mapping, cookielib, urljoin, urlparse 

19from .cookies import ( 

20 RequestsCookieJar, 

21 cookiejar_from_dict, 

22 extract_cookies_to_jar, 

23 merge_cookies, 

24) 

25from .exceptions import ( 

26 ChunkedEncodingError, 

27 ContentDecodingError, 

28 InvalidSchema, 

29 TooManyRedirects, 

30) 

31from .hooks import default_hooks, dispatch_hook 

32 

33# formerly defined here, reexposed here for backward compatibility 

34from .models import ( # noqa: F401 

35 DEFAULT_REDIRECT_LIMIT, 

36 REDIRECT_STATI, 

37 PreparedRequest, 

38 Request, 

39) 

40from .status_codes import codes 

41from .structures import CaseInsensitiveDict 

42from .utils import ( # noqa: F401 

43 DEFAULT_PORTS, 

44 default_headers, 

45 get_auth_from_url, 

46 get_environ_proxies, 

47 get_netrc_auth, 

48 requote_uri, 

49 resolve_proxies, 

50 rewind_body, 

51 should_bypass_proxies, 

52 to_key_val_list, 

53) 

54 

55# Preferred clock, based on which one is more accurate on a given system. 

56if sys.platform == "win32": 

57 preferred_clock = time.perf_counter 

58else: 

59 preferred_clock = time.time 

60 

61 

62def merge_setting(request_setting, session_setting, dict_class=OrderedDict): 

63 """Determines appropriate setting for a given request, taking into account 

64 the explicit setting on that request, and the setting in the session. If a 

65 setting is a dictionary, they will be merged together using `dict_class` 

66 """ 

67 

68 if session_setting is None: 

69 return request_setting 

70 

71 if request_setting is None: 

72 return session_setting 

73 

74 # Bypass if not a dictionary (e.g. verify) 

75 if not ( 

76 isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping) 

77 ): 

78 return request_setting 

79 

80 merged_setting = dict_class(to_key_val_list(session_setting)) 

81 merged_setting.update(to_key_val_list(request_setting)) 

82 

83 # Remove keys that are set to None. Extract keys first to avoid altering 

84 # the dictionary during iteration. 

85 none_keys = [k for (k, v) in merged_setting.items() if v is None] 

86 for key in none_keys: 

87 del merged_setting[key] 

88 

89 return merged_setting 

90 

91 

92def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): 

93 """Properly merges both requests and session hooks. 

94 

95 This is necessary because when request_hooks == {'response': []}, the 

96 merge breaks Session hooks entirely. 

97 """ 

98 if session_hooks is None or session_hooks.get("response") == []: 

99 return request_hooks 

100 

101 if request_hooks is None or request_hooks.get("response") == []: 

102 return session_hooks 

103 

104 return merge_setting(request_hooks, session_hooks, dict_class) 

105 

106 

107class SessionRedirectMixin: 

108 def get_redirect_target(self, resp): 

109 """Receives a Response. Returns a redirect URI or ``None``""" 

110 # Due to the nature of how requests processes redirects this method will 

111 # be called at least once upon the original response and at least twice 

112 # on each subsequent redirect response (if any). 

113 # If a custom mixin is used to handle this logic, it may be advantageous 

114 # to cache the redirect location onto the response object as a private 

115 # attribute. 

116 if resp.is_redirect: 

117 location = resp.headers["location"] 

118 # Currently the underlying http module on py3 decode headers 

119 # in latin1, but empirical evidence suggests that latin1 is very 

120 # rarely used with non-ASCII characters in HTTP headers. 

121 # It is more likely to get UTF8 header rather than latin1. 

122 # This causes incorrect handling of UTF8 encoded location headers. 

123 # To solve this, we re-encode the location in latin1. 

124 location = location.encode("latin1") 

125 return to_native_string(location, "utf8") 

126 return None 

127 

128 def should_strip_auth(self, old_url, new_url): 

129 """Decide whether Authorization header should be removed when redirecting""" 

130 old_parsed = urlparse(old_url) 

131 new_parsed = urlparse(new_url) 

132 if old_parsed.hostname != new_parsed.hostname: 

133 return True 

134 # Special case: allow http -> https redirect when using the standard 

135 # ports. This isn't specified by RFC 7235, but is kept to avoid 

136 # breaking backwards compatibility with older versions of requests 

137 # that allowed any redirects on the same host. 

138 if ( 

139 old_parsed.scheme == "http" 

140 and old_parsed.port in (80, None) 

141 and new_parsed.scheme == "https" 

142 and new_parsed.port in (443, None) 

143 ): 

144 return False 

145 

146 # Handle default port usage corresponding to scheme. 

147 changed_port = old_parsed.port != new_parsed.port 

148 changed_scheme = old_parsed.scheme != new_parsed.scheme 

149 default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) 

150 if ( 

151 not changed_scheme 

152 and old_parsed.port in default_port 

153 and new_parsed.port in default_port 

154 ): 

155 return False 

156 

157 # Standard case: root URI must match 

158 return changed_port or changed_scheme 

159 

160 def resolve_redirects( 

161 self, 

162 resp, 

163 req, 

164 stream=False, 

165 timeout=None, 

166 verify=True, 

167 cert=None, 

168 proxies=None, 

169 yield_requests=False, 

170 **adapter_kwargs, 

171 ): 

172 """Receives a Response. Returns a generator of Responses or Requests.""" 

173 

174 hist = [] # keep track of history 

175 

176 url = self.get_redirect_target(resp) 

177 previous_fragment = urlparse(req.url).fragment 

178 while url: 

179 prepared_request = req.copy() 

180 

181 # Update history and keep track of redirects. 

182 # resp.history must ignore the original request in this loop 

183 hist.append(resp) 

184 resp.history = hist[1:] 

185 

186 try: 

187 resp.content # Consume socket so it can be released 

188 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): 

189 resp.raw.read(decode_content=False) 

190 

191 if len(resp.history) >= self.max_redirects: 

192 raise TooManyRedirects( 

193 f"Exceeded {self.max_redirects} redirects.", response=resp 

194 ) 

195 

196 # Release the connection back into the pool. 

197 resp.close() 

198 

199 # Handle redirection without scheme (see: RFC 1808 Section 4) 

200 if url.startswith("//"): 

201 parsed_rurl = urlparse(resp.url) 

202 url = ":".join([to_native_string(parsed_rurl.scheme), url]) 

203 

204 # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) 

205 parsed = urlparse(url) 

206 if parsed.fragment == "" and previous_fragment: 

207 parsed = parsed._replace(fragment=previous_fragment) 

208 elif parsed.fragment: 

209 previous_fragment = parsed.fragment 

210 url = parsed.geturl() 

211 

212 # Facilitate relative 'location' headers, as allowed by RFC 7231. 

213 # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') 

214 # Compliant with RFC3986, we percent encode the url. 

215 if not parsed.netloc: 

216 url = urljoin(resp.url, requote_uri(url)) 

217 else: 

218 url = requote_uri(url) 

219 

220 prepared_request.url = to_native_string(url) 

221 

222 self.rebuild_method(prepared_request, resp) 

223 

224 # https://github.com/psf/requests/issues/1084 

225 if resp.status_code not in ( 

226 codes.temporary_redirect, 

227 codes.permanent_redirect, 

228 ): 

229 # https://github.com/psf/requests/issues/3490 

230 purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding") 

231 for header in purged_headers: 

232 prepared_request.headers.pop(header, None) 

233 prepared_request.body = None 

234 

235 headers = prepared_request.headers 

236 headers.pop("Cookie", None) 

237 

238 # Extract any cookies sent on the response to the cookiejar 

239 # in the new request. Because we've mutated our copied prepared 

240 # request, use the old one that we haven't yet touched. 

241 extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) 

242 merge_cookies(prepared_request._cookies, self.cookies) 

243 prepared_request.prepare_cookies(prepared_request._cookies) 

244 

245 # Rebuild auth and proxy information. 

246 proxies = self.rebuild_proxies(prepared_request, proxies) 

247 self.rebuild_auth(prepared_request, resp) 

248 

249 # A failed tell() sets `_body_position` to `object()`. This non-None 

250 # value ensures `rewindable` will be True, allowing us to raise an 

251 # UnrewindableBodyError, instead of hanging the connection. 

252 rewindable = prepared_request._body_position is not None and ( 

253 "Content-Length" in headers or "Transfer-Encoding" in headers 

254 ) 

255 

256 # Attempt to rewind consumed file-like object. 

257 if rewindable: 

258 rewind_body(prepared_request) 

259 

260 # Override the original request. 

261 req = prepared_request 

262 

263 if yield_requests: 

264 yield req 

265 else: 

266 resp = self.send( 

267 req, 

268 stream=stream, 

269 timeout=timeout, 

270 verify=verify, 

271 cert=cert, 

272 proxies=proxies, 

273 allow_redirects=False, 

274 **adapter_kwargs, 

275 ) 

276 

277 extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) 

278 

279 # extract redirect url, if any, for the next loop 

280 url = self.get_redirect_target(resp) 

281 yield resp 

282 

283 def rebuild_auth(self, prepared_request, response): 

284 """When being redirected we may want to strip authentication from the 

285 request to avoid leaking credentials. This method intelligently removes 

286 and reapplies authentication where possible to avoid credential loss. 

287 """ 

288 headers = prepared_request.headers 

289 url = prepared_request.url 

290 

291 if "Authorization" in headers and self.should_strip_auth( 

292 response.request.url, url 

293 ): 

294 # If we get redirected to a new host, we should strip out any 

295 # authentication headers. 

296 del headers["Authorization"] 

297 

298 # .netrc might have more auth for us on our new host. 

299 new_auth = get_netrc_auth(url) if self.trust_env else None 

300 if new_auth is not None: 

301 prepared_request.prepare_auth(new_auth) 

302 

303 def rebuild_proxies(self, prepared_request, proxies): 

304 """This method re-evaluates the proxy configuration by considering the 

305 environment variables. If we are redirected to a URL covered by 

306 NO_PROXY, we strip the proxy configuration. Otherwise, we set missing 

307 proxy keys for this URL (in case they were stripped by a previous 

308 redirect). 

309 

310 This method also replaces the Proxy-Authorization header where 

311 necessary. 

312 

313 :rtype: dict 

314 """ 

315 headers = prepared_request.headers 

316 scheme = urlparse(prepared_request.url).scheme 

317 new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env) 

318 

319 if "Proxy-Authorization" in headers: 

320 del headers["Proxy-Authorization"] 

321 

322 try: 

323 username, password = get_auth_from_url(new_proxies[scheme]) 

324 except KeyError: 

325 username, password = None, None 

326 

327 # urllib3 handles proxy authorization for us in the standard adapter. 

328 # Avoid appending this to TLS tunneled requests where it may be leaked. 

329 if not scheme.startswith("https") and username and password: 

330 headers["Proxy-Authorization"] = _basic_auth_str(username, password) 

331 

332 return new_proxies 

333 

334 def rebuild_method(self, prepared_request, response): 

335 """When being redirected we may want to change the method of the request 

336 based on certain specs or browser behavior. 

337 """ 

338 method = prepared_request.method 

339 

340 # https://tools.ietf.org/html/rfc7231#section-6.4.4 

341 if response.status_code == codes.see_other and method != "HEAD": 

342 method = "GET" 

343 

344 # Do what the browsers do, despite standards... 

345 # First, turn 302s into GETs. 

346 if response.status_code == codes.found and method != "HEAD": 

347 method = "GET" 

348 

349 # Second, if a POST is responded to with a 301, turn it into a GET. 

350 # This bizarre behaviour is explained in Issue 1704. 

351 if response.status_code == codes.moved and method == "POST": 

352 method = "GET" 

353 

354 prepared_request.method = method 

355 

356 

357class Session(SessionRedirectMixin): 

358 """A Requests session. 

359 

360 Provides cookie persistence, connection-pooling, and configuration. 

361 

362 Basic Usage:: 

363 

364 >>> import requests 

365 >>> s = requests.Session() 

366 >>> s.get('https://httpbin.org/get') 

367 <Response [200]> 

368 

369 Or as a context manager:: 

370 

371 >>> with requests.Session() as s: 

372 ... s.get('https://httpbin.org/get') 

373 <Response [200]> 

374 """ 

375 

376 __attrs__ = [ 

377 "headers", 

378 "cookies", 

379 "auth", 

380 "proxies", 

381 "hooks", 

382 "params", 

383 "verify", 

384 "cert", 

385 "adapters", 

386 "stream", 

387 "trust_env", 

388 "max_redirects", 

389 ] 

390 

391 def __init__(self): 

392 #: A case-insensitive dictionary of headers to be sent on each 

393 #: :class:`Request <Request>` sent from this 

394 #: :class:`Session <Session>`. 

395 self.headers = default_headers() 

396 

397 #: Default Authentication tuple or object to attach to 

398 #: :class:`Request <Request>`. 

399 self.auth = None 

400 

401 #: Dictionary mapping protocol or protocol and host to the URL of the proxy 

402 #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to 

403 #: be used on each :class:`Request <Request>`. 

404 self.proxies = {} 

405 

406 #: Event-handling hooks. 

407 self.hooks = default_hooks() 

408 

409 #: Dictionary of querystring data to attach to each 

410 #: :class:`Request <Request>`. The dictionary values may be lists for 

411 #: representing multivalued query parameters. 

412 self.params = {} 

413 

414 #: Stream response content default. 

415 self.stream = False 

416 

417 #: SSL Verification default. 

418 #: Defaults to `True`, requiring requests to verify the TLS certificate at the 

419 #: remote end. 

420 #: If verify is set to `False`, requests will accept any TLS certificate 

421 #: presented by the server, and will ignore hostname mismatches and/or 

422 #: expired certificates, which will make your application vulnerable to 

423 #: man-in-the-middle (MitM) attacks. 

424 #: Only set this to `False` for testing. 

425 self.verify = True 

426 

427 #: SSL client certificate default, if String, path to ssl client 

428 #: cert file (.pem). If Tuple, ('cert', 'key') pair. 

429 self.cert = None 

430 

431 #: Maximum number of redirects allowed. If the request exceeds this 

432 #: limit, a :class:`TooManyRedirects` exception is raised. 

433 #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is 

434 #: 30. 

435 self.max_redirects = DEFAULT_REDIRECT_LIMIT 

436 

437 #: Trust environment settings for proxy configuration, default 

438 #: authentication and similar. 

439 self.trust_env = True 

440 

441 #: A CookieJar containing all currently outstanding cookies set on this 

442 #: session. By default it is a 

443 #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but 

444 #: may be any other ``cookielib.CookieJar`` compatible object. 

445 self.cookies = cookiejar_from_dict({}) 

446 

447 # Default connection adapters. 

448 self.adapters = OrderedDict() 

449 self.mount("https://", HTTPAdapter()) 

450 self.mount("http://", HTTPAdapter()) 

451 

452 def __enter__(self): 

453 return self 

454 

455 def __exit__(self, *args): 

456 self.close() 

457 

458 def prepare_request(self, request): 

459 """Constructs a :class:`PreparedRequest <PreparedRequest>` for 

460 transmission and returns it. The :class:`PreparedRequest` has settings 

461 merged from the :class:`Request <Request>` instance and those of the 

462 :class:`Session`. 

463 

464 :param request: :class:`Request` instance to prepare with this 

465 session's settings. 

466 :rtype: requests.PreparedRequest 

467 """ 

468 cookies = request.cookies or {} 

469 

470 # Bootstrap CookieJar. 

471 if not isinstance(cookies, cookielib.CookieJar): 

472 cookies = cookiejar_from_dict(cookies) 

473 

474 # Merge with session cookies 

475 merged_cookies = merge_cookies( 

476 merge_cookies(RequestsCookieJar(), self.cookies), cookies 

477 ) 

478 

479 # Set environment's basic authentication if not explicitly set. 

480 auth = request.auth 

481 if self.trust_env and not auth and not self.auth: 

482 auth = get_netrc_auth(request.url) 

483 

484 p = PreparedRequest() 

485 p.prepare( 

486 method=request.method.upper(), 

487 url=request.url, 

488 files=request.files, 

489 data=request.data, 

490 json=request.json, 

491 headers=merge_setting( 

492 request.headers, self.headers, dict_class=CaseInsensitiveDict 

493 ), 

494 params=merge_setting(request.params, self.params), 

495 auth=merge_setting(auth, self.auth), 

496 cookies=merged_cookies, 

497 hooks=merge_hooks(request.hooks, self.hooks), 

498 ) 

499 return p 

500 

501 def request( 

502 self, 

503 method, 

504 url, 

505 params=None, 

506 data=None, 

507 headers=None, 

508 cookies=None, 

509 files=None, 

510 auth=None, 

511 timeout=None, 

512 allow_redirects=True, 

513 proxies=None, 

514 hooks=None, 

515 stream=None, 

516 verify=None, 

517 cert=None, 

518 json=None, 

519 ): 

520 """Constructs a :class:`Request <Request>`, prepares it and sends it. 

521 Returns :class:`Response <Response>` object. 

522 

523 :param method: method for the new :class:`Request` object. 

524 :param url: URL for the new :class:`Request` object. 

525 :param params: (optional) Dictionary or bytes to be sent in the query 

526 string for the :class:`Request`. 

527 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

528 object to send in the body of the :class:`Request`. 

529 :param json: (optional) json to send in the body of the 

530 :class:`Request`. 

531 :param headers: (optional) Dictionary of HTTP Headers to send with the 

532 :class:`Request`. 

533 :param cookies: (optional) Dict or CookieJar object to send with the 

534 :class:`Request`. 

535 :param files: (optional) Dictionary of ``'filename': file-like-objects`` 

536 for multipart encoding upload. 

537 :param auth: (optional) Auth tuple or callable to enable 

538 Basic/Digest/Custom HTTP Auth. 

539 :param timeout: (optional) How many seconds to wait for the server to send 

540 data before giving up, as a float, or a :ref:`(connect timeout, 

541 read timeout) <timeouts>` tuple. 

542 :type timeout: float or tuple 

543 :param allow_redirects: (optional) Set to True by default. 

544 :type allow_redirects: bool 

545 :param proxies: (optional) Dictionary mapping protocol or protocol and 

546 hostname to the URL of the proxy. 

547 :param hooks: (optional) Dictionary mapping hook name to one event or 

548 list of events, event must be callable. 

549 :param stream: (optional) whether to immediately download the response 

550 content. Defaults to ``False``. 

551 :param verify: (optional) Either a boolean, in which case it controls whether we verify 

552 the server's TLS certificate, or a string, in which case it must be a path 

553 to a CA bundle to use. Defaults to ``True``. When set to 

554 ``False``, requests will accept any TLS certificate presented by 

555 the server, and will ignore hostname mismatches and/or expired 

556 certificates, which will make your application vulnerable to 

557 man-in-the-middle (MitM) attacks. Setting verify to ``False`` 

558 may be useful during local development or testing. 

559 :param cert: (optional) if String, path to ssl client cert file (.pem). 

560 If Tuple, ('cert', 'key') pair. 

561 :rtype: requests.Response 

562 """ 

563 # Create the Request. 

564 req = Request( 

565 method=method.upper(), 

566 url=url, 

567 headers=headers, 

568 files=files, 

569 data=data or {}, 

570 json=json, 

571 params=params or {}, 

572 auth=auth, 

573 cookies=cookies, 

574 hooks=hooks, 

575 ) 

576 prep = self.prepare_request(req) 

577 

578 proxies = proxies or {} 

579 

580 settings = self.merge_environment_settings( 

581 prep.url, proxies, stream, verify, cert 

582 ) 

583 

584 # Send the request. 

585 send_kwargs = { 

586 "timeout": timeout, 

587 "allow_redirects": allow_redirects, 

588 } 

589 send_kwargs.update(settings) 

590 resp = self.send(prep, **send_kwargs) 

591 

592 return resp 

593 

594 def get(self, url, **kwargs): 

595 r"""Sends a GET request. Returns :class:`Response` object. 

596 

597 :param url: URL for the new :class:`Request` object. 

598 :param \*\*kwargs: Optional arguments that ``request`` takes. 

599 :rtype: requests.Response 

600 """ 

601 

602 kwargs.setdefault("allow_redirects", True) 

603 return self.request("GET", url, **kwargs) 

604 

605 def options(self, url, **kwargs): 

606 r"""Sends a OPTIONS request. Returns :class:`Response` object. 

607 

608 :param url: URL for the new :class:`Request` object. 

609 :param \*\*kwargs: Optional arguments that ``request`` takes. 

610 :rtype: requests.Response 

611 """ 

612 

613 kwargs.setdefault("allow_redirects", True) 

614 return self.request("OPTIONS", url, **kwargs) 

615 

616 def head(self, url, **kwargs): 

617 r"""Sends a HEAD request. Returns :class:`Response` object. 

618 

619 :param url: URL for the new :class:`Request` object. 

620 :param \*\*kwargs: Optional arguments that ``request`` takes. 

621 :rtype: requests.Response 

622 """ 

623 

624 kwargs.setdefault("allow_redirects", False) 

625 return self.request("HEAD", url, **kwargs) 

626 

627 def post(self, url, data=None, json=None, **kwargs): 

628 r"""Sends a POST request. Returns :class:`Response` object. 

629 

630 :param url: URL for the new :class:`Request` object. 

631 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

632 object to send in the body of the :class:`Request`. 

633 :param json: (optional) json to send in the body of the :class:`Request`. 

634 :param \*\*kwargs: Optional arguments that ``request`` takes. 

635 :rtype: requests.Response 

636 """ 

637 

638 return self.request("POST", url, data=data, json=json, **kwargs) 

639 

640 def put(self, url, data=None, **kwargs): 

641 r"""Sends a PUT request. Returns :class:`Response` object. 

642 

643 :param url: URL for the new :class:`Request` object. 

644 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

645 object to send in the body of the :class:`Request`. 

646 :param \*\*kwargs: Optional arguments that ``request`` takes. 

647 :rtype: requests.Response 

648 """ 

649 

650 return self.request("PUT", url, data=data, **kwargs) 

651 

652 def patch(self, url, data=None, **kwargs): 

653 r"""Sends a PATCH request. Returns :class:`Response` object. 

654 

655 :param url: URL for the new :class:`Request` object. 

656 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 

657 object to send in the body of the :class:`Request`. 

658 :param \*\*kwargs: Optional arguments that ``request`` takes. 

659 :rtype: requests.Response 

660 """ 

661 

662 return self.request("PATCH", url, data=data, **kwargs) 

663 

664 def delete(self, url, **kwargs): 

665 r"""Sends a DELETE request. Returns :class:`Response` object. 

666 

667 :param url: URL for the new :class:`Request` object. 

668 :param \*\*kwargs: Optional arguments that ``request`` takes. 

669 :rtype: requests.Response 

670 """ 

671 

672 return self.request("DELETE", url, **kwargs) 

673 

674 def send(self, request, **kwargs): 

675 """Send a given PreparedRequest. 

676 

677 :rtype: requests.Response 

678 """ 

679 # Set defaults that the hooks can utilize to ensure they always have 

680 # the correct parameters to reproduce the previous request. 

681 kwargs.setdefault("stream", self.stream) 

682 kwargs.setdefault("verify", self.verify) 

683 kwargs.setdefault("cert", self.cert) 

684 if "proxies" not in kwargs: 

685 kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env) 

686 

687 # It's possible that users might accidentally send a Request object. 

688 # Guard against that specific failure case. 

689 if isinstance(request, Request): 

690 raise ValueError("You can only send PreparedRequests.") 

691 

692 # Set up variables needed for resolve_redirects and dispatching of hooks 

693 allow_redirects = kwargs.pop("allow_redirects", True) 

694 stream = kwargs.get("stream") 

695 hooks = request.hooks 

696 

697 # Get the appropriate adapter to use 

698 adapter = self.get_adapter(url=request.url) 

699 

700 # Start time (approximately) of the request 

701 start = preferred_clock() 

702 

703 # Send the request 

704 r = adapter.send(request, **kwargs) 

705 

706 # Total elapsed time of the request (approximately) 

707 elapsed = preferred_clock() - start 

708 r.elapsed = timedelta(seconds=elapsed) 

709 

710 # Response manipulation hooks 

711 r = dispatch_hook("response", hooks, r, **kwargs) 

712 

713 # Persist cookies 

714 if r.history: 

715 # If the hooks create history then we want those cookies too 

716 for resp in r.history: 

717 extract_cookies_to_jar(self.cookies, resp.request, resp.raw) 

718 

719 extract_cookies_to_jar(self.cookies, request, r.raw) 

720 

721 # Resolve redirects if allowed. 

722 if allow_redirects: 

723 # Redirect resolving generator. 

724 gen = self.resolve_redirects(r, request, **kwargs) 

725 history = [resp for resp in gen] 

726 else: 

727 history = [] 

728 

729 # Shuffle things around if there's history. 

730 if history: 

731 # Insert the first (original) request at the start 

732 history.insert(0, r) 

733 # Get the last request made 

734 r = history.pop() 

735 r.history = history 

736 

737 # If redirects aren't being followed, store the response on the Request for Response.next(). 

738 if not allow_redirects: 

739 try: 

740 r._next = next( 

741 self.resolve_redirects(r, request, yield_requests=True, **kwargs) 

742 ) 

743 except StopIteration: 

744 pass 

745 

746 if not stream: 

747 r.content 

748 

749 return r 

750 

751 def merge_environment_settings(self, url, proxies, stream, verify, cert): 

752 """ 

753 Check the environment and merge it with some settings. 

754 

755 :rtype: dict 

756 """ 

757 # Gather clues from the surrounding environment. 

758 if self.trust_env: 

759 # Set environment's proxies. 

760 no_proxy = proxies.get("no_proxy") if proxies is not None else None 

761 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) 

762 for k, v in env_proxies.items(): 

763 proxies.setdefault(k, v) 

764 

765 # Look for requests environment configuration 

766 # and be compatible with cURL. 

767 if verify is True or verify is None: 

768 verify = ( 

769 os.environ.get("REQUESTS_CA_BUNDLE") 

770 or os.environ.get("CURL_CA_BUNDLE") 

771 or verify 

772 ) 

773 

774 # Merge all the kwargs. 

775 proxies = merge_setting(proxies, self.proxies) 

776 stream = merge_setting(stream, self.stream) 

777 verify = merge_setting(verify, self.verify) 

778 cert = merge_setting(cert, self.cert) 

779 

780 return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert} 

781 

782 def get_adapter(self, url): 

783 """ 

784 Returns the appropriate connection adapter for the given URL. 

785 

786 :rtype: requests.adapters.BaseAdapter 

787 """ 

788 for prefix, adapter in self.adapters.items(): 

789 if url.lower().startswith(prefix.lower()): 

790 return adapter 

791 

792 # Nothing matches :-/ 

793 raise InvalidSchema(f"No connection adapters were found for {url!r}") 

794 

795 def close(self): 

796 """Closes all adapters and as such the session""" 

797 for v in self.adapters.values(): 

798 v.close() 

799 

800 def mount(self, prefix, adapter): 

801 """Registers a connection adapter to a prefix. 

802 

803 Adapters are sorted in descending order by prefix length. 

804 """ 

805 self.adapters[prefix] = adapter 

806 keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] 

807 

808 for key in keys_to_move: 

809 self.adapters[key] = self.adapters.pop(key) 

810 

811 def __getstate__(self): 

812 state = {attr: getattr(self, attr, None) for attr in self.__attrs__} 

813 return state 

814 

815 def __setstate__(self, state): 

816 for attr, value in state.items(): 

817 setattr(self, attr, value) 

818 

819 

820def session(): 

821 """ 

822 Returns a :class:`Session` for context-management. 

823 

824 .. deprecated:: 1.0.0 

825 

826 This method has been deprecated since version 1.0.0 and is only kept for 

827 backwards compatibility. New code should use :class:`~requests.sessions.Session` 

828 to create a session. This may be removed at a future date. 

829 

830 :rtype: Session 

831 """ 

832 return Session()