Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/requests/sessions.py: 15%

268 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2requests.sessions 

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

4 

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

6requests (cookies, auth, proxies). 

7""" 

8import os 

9import sys 

10import time 

11from collections import OrderedDict 

12from datetime import timedelta 

13 

14from ._internal_utils import to_native_string 

15from .adapters import HTTPAdapter 

16from .auth import _basic_auth_str 

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

18from .cookies import ( 

19 RequestsCookieJar, 

20 cookiejar_from_dict, 

21 extract_cookies_to_jar, 

22 merge_cookies, 

23) 

24from .exceptions import ( 

25 ChunkedEncodingError, 

26 ContentDecodingError, 

27 InvalidSchema, 

28 TooManyRedirects, 

29) 

30from .hooks import default_hooks, dispatch_hook 

31 

32# formerly defined here, reexposed here for backward compatibility 

33from .models import ( # noqa: F401 

34 DEFAULT_REDIRECT_LIMIT, 

35 REDIRECT_STATI, 

36 PreparedRequest, 

37 Request, 

38) 

39from .status_codes import codes 

40from .structures import CaseInsensitiveDict 

41from .utils import ( # noqa: F401 

42 DEFAULT_PORTS, 

43 default_headers, 

44 get_auth_from_url, 

45 get_environ_proxies, 

46 get_netrc_auth, 

47 requote_uri, 

48 resolve_proxies, 

49 rewind_body, 

50 should_bypass_proxies, 

51 to_key_val_list, 

52) 

53 

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

55if sys.platform == "win32": 55 ↛ 56line 55 didn't jump to line 56, because the condition on line 55 was never true

56 preferred_clock = time.perf_counter 

57else: 

58 preferred_clock = time.time 

59 

60 

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

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

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

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

65 """ 

66 

67 if session_setting is None: 

68 return request_setting 

69 

70 if request_setting is None: 

71 return session_setting 

72 

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

74 if not ( 

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

76 ): 

77 return request_setting 

78 

79 merged_setting = dict_class(to_key_val_list(session_setting)) 

80 merged_setting.update(to_key_val_list(request_setting)) 

81 

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

83 # the dictionary during iteration. 

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

85 for key in none_keys: 

86 del merged_setting[key] 

87 

88 return merged_setting 

89 

90 

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

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

93 

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

95 merge breaks Session hooks entirely. 

96 """ 

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

98 return request_hooks 

99 

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

101 return session_hooks 

102 

103 return merge_setting(request_hooks, session_hooks, dict_class) 

104 

105 

106class SessionRedirectMixin: 

107 def get_redirect_target(self, resp): 

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

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

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

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

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

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

114 # attribute. 

115 if resp.is_redirect: 

116 location = resp.headers["location"] 

117 # Currently the underlying http module on py3 decode headers 

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

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

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

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

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

123 location = location.encode("latin1") 

124 return to_native_string(location, "utf8") 

125 return None 

126 

127 def should_strip_auth(self, old_url, new_url): 

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

129 old_parsed = urlparse(old_url) 

130 new_parsed = urlparse(new_url) 

131 if old_parsed.hostname != new_parsed.hostname: 

132 return True 

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

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

135 # breaking backwards compatibility with older versions of requests 

136 # that allowed any redirects on the same host. 

137 if ( 

138 old_parsed.scheme == "http" 

139 and old_parsed.port in (80, None) 

140 and new_parsed.scheme == "https" 

141 and new_parsed.port in (443, None) 

142 ): 

143 return False 

144 

145 # Handle default port usage corresponding to scheme. 

146 changed_port = old_parsed.port != new_parsed.port 

147 changed_scheme = old_parsed.scheme != new_parsed.scheme 

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

149 if ( 

150 not changed_scheme 

151 and old_parsed.port in default_port 

152 and new_parsed.port in default_port 

153 ): 

154 return False 

155 

156 # Standard case: root URI must match 

157 return changed_port or changed_scheme 

158 

159 def resolve_redirects( 

160 self, 

161 resp, 

162 req, 

163 stream=False, 

164 timeout=None, 

165 verify=True, 

166 cert=None, 

167 proxies=None, 

168 yield_requests=False, 

169 **adapter_kwargs, 

170 ): 

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

172 

173 hist = [] # keep track of history 

174 

175 url = self.get_redirect_target(resp) 

176 previous_fragment = urlparse(req.url).fragment 

177 while url: 

178 prepared_request = req.copy() 

179 

180 # Update history and keep track of redirects. 

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

182 hist.append(resp) 

183 resp.history = hist[1:] 

184 

185 try: 

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

187 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): 

188 resp.raw.read(decode_content=False) 

189 

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

191 raise TooManyRedirects( 

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

193 ) 

194 

195 # Release the connection back into the pool. 

196 resp.close() 

197 

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

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

200 parsed_rurl = urlparse(resp.url) 

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

202 

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

204 parsed = urlparse(url) 

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

206 parsed = parsed._replace(fragment=previous_fragment) 

207 elif parsed.fragment: 

208 previous_fragment = parsed.fragment 

209 url = parsed.geturl() 

210 

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

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

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

214 if not parsed.netloc: 

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

216 else: 

217 url = requote_uri(url) 

218 

219 prepared_request.url = to_native_string(url) 

220 

221 self.rebuild_method(prepared_request, resp) 

222 

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

224 if resp.status_code not in ( 

225 codes.temporary_redirect, 

226 codes.permanent_redirect, 

227 ): 

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

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

230 for header in purged_headers: 

231 prepared_request.headers.pop(header, None) 

232 prepared_request.body = None 

233 

234 headers = prepared_request.headers 

235 headers.pop("Cookie", None) 

236 

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

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

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

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

241 merge_cookies(prepared_request._cookies, self.cookies) 

242 prepared_request.prepare_cookies(prepared_request._cookies) 

243 

244 # Rebuild auth and proxy information. 

245 proxies = self.rebuild_proxies(prepared_request, proxies) 

246 self.rebuild_auth(prepared_request, resp) 

247 

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

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

250 # UnrewindableBodyError, instead of hanging the connection. 

251 rewindable = prepared_request._body_position is not None and ( 

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

253 ) 

254 

255 # Attempt to rewind consumed file-like object. 

256 if rewindable: 

257 rewind_body(prepared_request) 

258 

259 # Override the original request. 

260 req = prepared_request 

261 

262 if yield_requests: 

263 yield req 

264 else: 

265 

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 if username and password: 

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

329 

330 return new_proxies 

331 

332 def rebuild_method(self, prepared_request, response): 

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

334 based on certain specs or browser behavior. 

335 """ 

336 method = prepared_request.method 

337 

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

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

340 method = "GET" 

341 

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

343 # First, turn 302s into GETs. 

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

345 method = "GET" 

346 

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

348 # This bizarre behaviour is explained in Issue 1704. 

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

350 method = "GET" 

351 

352 prepared_request.method = method 

353 

354 

355class Session(SessionRedirectMixin): 

356 """A Requests session. 

357 

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

359 

360 Basic Usage:: 

361 

362 >>> import requests 

363 >>> s = requests.Session() 

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

365 <Response [200]> 

366 

367 Or as a context manager:: 

368 

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

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

371 <Response [200]> 

372 """ 

373 

374 __attrs__ = [ 

375 "headers", 

376 "cookies", 

377 "auth", 

378 "proxies", 

379 "hooks", 

380 "params", 

381 "verify", 

382 "cert", 

383 "adapters", 

384 "stream", 

385 "trust_env", 

386 "max_redirects", 

387 ] 

388 

389 def __init__(self): 

390 

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

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

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

394 self.headers = default_headers() 

395 

396 #: Default Authentication tuple or object to attach to 

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

398 self.auth = None 

399 

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

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

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

403 self.proxies = {} 

404 

405 #: Event-handling hooks. 

406 self.hooks = default_hooks() 

407 

408 #: Dictionary of querystring data to attach to each 

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

410 #: representing multivalued query parameters. 

411 self.params = {} 

412 

413 #: Stream response content default. 

414 self.stream = False 

415 

416 #: SSL Verification default. 

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

418 #: remote end. 

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

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

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

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

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

424 self.verify = True 

425 

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

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

428 self.cert = None 

429 

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

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

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

433 #: 30. 

434 self.max_redirects = DEFAULT_REDIRECT_LIMIT 

435 

436 #: Trust environment settings for proxy configuration, default 

437 #: authentication and similar. 

438 self.trust_env = True 

439 

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

441 #: session. By default it is a 

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

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

444 self.cookies = cookiejar_from_dict({}) 

445 

446 # Default connection adapters. 

447 self.adapters = OrderedDict() 

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

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

450 

451 def __enter__(self): 

452 return self 

453 

454 def __exit__(self, *args): 

455 self.close() 

456 

457 def prepare_request(self, request): 

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

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

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

461 :class:`Session`. 

462 

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

464 session's settings. 

465 :rtype: requests.PreparedRequest 

466 """ 

467 cookies = request.cookies or {} 

468 

469 # Bootstrap CookieJar. 

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

471 cookies = cookiejar_from_dict(cookies) 

472 

473 # Merge with session cookies 

474 merged_cookies = merge_cookies( 

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

476 ) 

477 

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

479 auth = request.auth 

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

481 auth = get_netrc_auth(request.url) 

482 

483 p = PreparedRequest() 

484 p.prepare( 

485 method=request.method.upper(), 

486 url=request.url, 

487 files=request.files, 

488 data=request.data, 

489 json=request.json, 

490 headers=merge_setting( 

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

492 ), 

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

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

495 cookies=merged_cookies, 

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

497 ) 

498 return p 

499 

500 def request( 

501 self, 

502 method, 

503 url, 

504 params=None, 

505 data=None, 

506 headers=None, 

507 cookies=None, 

508 files=None, 

509 auth=None, 

510 timeout=None, 

511 allow_redirects=True, 

512 proxies=None, 

513 hooks=None, 

514 stream=None, 

515 verify=None, 

516 cert=None, 

517 json=None, 

518 ): 

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

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

521 

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

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

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

525 string for the :class:`Request`. 

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

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

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

529 :class:`Request`. 

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

531 :class:`Request`. 

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

533 :class:`Request`. 

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

535 for multipart encoding upload. 

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

537 Basic/Digest/Custom HTTP Auth. 

538 :param timeout: (optional) How long to wait for the server to send 

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

540 read timeout) <timeouts>` tuple. 

541 :type timeout: float or tuple 

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

543 :type allow_redirects: bool 

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

545 hostname to the URL of the proxy. 

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

547 content. Defaults to ``False``. 

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

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

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

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

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

553 certificates, which will make your application vulnerable to 

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

555 may be useful during local development or testing. 

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

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

558 :rtype: requests.Response 

559 """ 

560 # Create the Request. 

561 req = Request( 

562 method=method.upper(), 

563 url=url, 

564 headers=headers, 

565 files=files, 

566 data=data or {}, 

567 json=json, 

568 params=params or {}, 

569 auth=auth, 

570 cookies=cookies, 

571 hooks=hooks, 

572 ) 

573 prep = self.prepare_request(req) 

574 

575 proxies = proxies or {} 

576 

577 settings = self.merge_environment_settings( 

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

579 ) 

580 

581 # Send the request. 

582 send_kwargs = { 

583 "timeout": timeout, 

584 "allow_redirects": allow_redirects, 

585 } 

586 send_kwargs.update(settings) 

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

588 

589 return resp 

590 

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

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

593 

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

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

596 :rtype: requests.Response 

597 """ 

598 

599 kwargs.setdefault("allow_redirects", True) 

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

601 

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

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

604 

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

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

607 :rtype: requests.Response 

608 """ 

609 

610 kwargs.setdefault("allow_redirects", True) 

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

612 

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

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

615 

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

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

618 :rtype: requests.Response 

619 """ 

620 

621 kwargs.setdefault("allow_redirects", False) 

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

623 

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

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

626 

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

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

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

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

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

632 :rtype: requests.Response 

633 """ 

634 

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

636 

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

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

639 

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

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

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

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

644 :rtype: requests.Response 

645 """ 

646 

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

648 

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

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

651 

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

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

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

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

656 :rtype: requests.Response 

657 """ 

658 

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

660 

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

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

663 

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

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

666 :rtype: requests.Response 

667 """ 

668 

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

670 

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

672 """Send a given PreparedRequest. 

673 

674 :rtype: requests.Response 

675 """ 

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

677 # the correct parameters to reproduce the previous request. 

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

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

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

681 if "proxies" not in kwargs: 

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

683 

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

685 # Guard against that specific failure case. 

686 if isinstance(request, Request): 

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

688 

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

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

691 stream = kwargs.get("stream") 

692 hooks = request.hooks 

693 

694 # Get the appropriate adapter to use 

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

696 

697 # Start time (approximately) of the request 

698 start = preferred_clock() 

699 

700 # Send the request 

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

702 

703 # Total elapsed time of the request (approximately) 

704 elapsed = preferred_clock() - start 

705 r.elapsed = timedelta(seconds=elapsed) 

706 

707 # Response manipulation hooks 

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

709 

710 # Persist cookies 

711 if r.history: 

712 

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

714 for resp in r.history: 

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

716 

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

718 

719 # Resolve redirects if allowed. 

720 if allow_redirects: 

721 # Redirect resolving generator. 

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

723 history = [resp for resp in gen] 

724 else: 

725 history = [] 

726 

727 # Shuffle things around if there's history. 

728 if history: 

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

730 history.insert(0, r) 

731 # Get the last request made 

732 r = history.pop() 

733 r.history = history 

734 

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

736 if not allow_redirects: 

737 try: 

738 r._next = next( 

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

740 ) 

741 except StopIteration: 

742 pass 

743 

744 if not stream: 

745 r.content 

746 

747 return r 

748 

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

750 """ 

751 Check the environment and merge it with some settings. 

752 

753 :rtype: dict 

754 """ 

755 # Gather clues from the surrounding environment. 

756 if self.trust_env: 

757 # Set environment's proxies. 

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

759 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) 

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

761 proxies.setdefault(k, v) 

762 

763 # Look for requests environment configuration 

764 # and be compatible with cURL. 

765 if verify is True or verify is None: 

766 verify = ( 

767 os.environ.get("REQUESTS_CA_BUNDLE") 

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

769 or verify 

770 ) 

771 

772 # Merge all the kwargs. 

773 proxies = merge_setting(proxies, self.proxies) 

774 stream = merge_setting(stream, self.stream) 

775 verify = merge_setting(verify, self.verify) 

776 cert = merge_setting(cert, self.cert) 

777 

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

779 

780 def get_adapter(self, url): 

781 """ 

782 Returns the appropriate connection adapter for the given URL. 

783 

784 :rtype: requests.adapters.BaseAdapter 

785 """ 

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

787 

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

789 return adapter 

790 

791 # Nothing matches :-/ 

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

793 

794 def close(self): 

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

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

797 v.close() 

798 

799 def mount(self, prefix, adapter): 

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

801 

802 Adapters are sorted in descending order by prefix length. 

803 """ 

804 self.adapters[prefix] = adapter 

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

806 

807 for key in keys_to_move: 

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

809 

810 def __getstate__(self): 

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

812 return state 

813 

814 def __setstate__(self, state): 

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

816 setattr(self, attr, value) 

817 

818 

819def session(): 

820 """ 

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

822 

823 .. deprecated:: 1.0.0 

824 

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

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

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

828 

829 :rtype: Session 

830 """ 

831 return Session()