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

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

307 statements  

1""" 

2requests.sessions 

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

4 

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

6requests (cookies, auth, proxies). 

7""" 

8 

9from __future__ import annotations 

10 

11import os 

12import sys 

13import time 

14from collections import OrderedDict 

15from collections.abc import Generator, Mapping, MutableMapping 

16from datetime import timedelta 

17from typing import TYPE_CHECKING, Any, cast 

18 

19from ._internal_utils import to_native_string 

20from ._types import is_prepared as _is_prepared 

21from .adapters import HTTPAdapter 

22from .auth import _basic_auth_str # type: ignore[reportPrivateUsage] 

23from .compat import cookielib, urljoin, urlparse 

24from .cookies import ( 

25 RequestsCookieJar, 

26 cookiejar_from_dict, 

27 extract_cookies_to_jar, 

28 merge_cookies, 

29) 

30from .exceptions import ( 

31 ChunkedEncodingError, 

32 ContentDecodingError, 

33 InvalidSchema, 

34 TooManyRedirects, 

35) 

36from .hooks import default_hooks, dispatch_hook 

37 

38# formerly defined here, reexposed here for backward compatibility 

39from .models import ( # noqa: F401 

40 DEFAULT_REDIRECT_LIMIT, 

41 REDIRECT_STATI, # type: ignore[reportUnusedImport] 

42 PreparedRequest, 

43 Request, 

44 Response, 

45) 

46from .status_codes import codes 

47from .structures import CaseInsensitiveDict 

48from .utils import ( # noqa: F401 

49 DEFAULT_PORTS, 

50 default_headers, 

51 get_auth_from_url, 

52 get_environ_proxies, 

53 get_netrc_auth, 

54 requote_uri, 

55 resolve_proxies, 

56 rewind_body, 

57 should_bypass_proxies, # type: ignore[reportUnusedImport] # re-export for external consumers 

58 to_key_val_list, 

59) 

60 

61if TYPE_CHECKING: 

62 from http.cookiejar import CookieJar 

63 

64 from typing_extensions import Self, Unpack 

65 

66 from . import _types as _t 

67 from .adapters import BaseAdapter 

68 

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

70if sys.platform == "win32": 

71 preferred_clock = time.perf_counter 

72else: 

73 preferred_clock = time.time 

74 

75 

76def merge_setting( 

77 request_setting: Any, session_setting: Any, dict_class: type = OrderedDict 

78) -> Any: 

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

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

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

82 """ 

83 

84 if session_setting is None: 

85 return request_setting 

86 

87 if request_setting is None: 

88 return session_setting 

89 

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

91 if not ( 

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

93 ): 

94 return request_setting 

95 

96 merged_setting = dict_class(to_key_val_list(session_setting)) # type: ignore[arg-type] # isinstance narrows Any to Mapping[Unknown] 

97 merged_setting.update(to_key_val_list(request_setting)) # type: ignore[arg-type] 

98 

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

100 # the dictionary during iteration. 

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

102 for key in none_keys: 

103 del merged_setting[key] 

104 

105 return merged_setting 

106 

107 

108def merge_hooks( 

109 request_hooks: _t.HooksType, 

110 session_hooks: _t.HooksType, 

111 dict_class: type = OrderedDict, 

112) -> _t.HooksType: 

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

114 

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

116 merge breaks Session hooks entirely. 

117 """ 

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

119 return request_hooks 

120 

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

122 return session_hooks 

123 

124 return merge_setting(request_hooks, session_hooks, dict_class) 

125 

126 

127class SessionRedirectMixin: 

128 max_redirects: int 

129 trust_env: bool 

130 cookies: RequestsCookieJar 

131 

132 def send(self, request: PreparedRequest, **kwargs: Any) -> Response: ... 

133 

134 def get_redirect_target(self, resp: Response) -> str | None: 

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

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

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

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

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

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

141 # attribute. 

142 if resp.is_redirect: 

143 location = resp.headers["location"] 

144 # Currently the underlying http module on py3 decode headers 

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

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

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

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

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

150 location = location.encode("latin1") 

151 return to_native_string(location, "utf8") 

152 return None 

153 

154 def should_strip_auth(self, old_url: str, new_url: str) -> bool: 

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

156 old_parsed = urlparse(old_url) 

157 new_parsed = urlparse(new_url) 

158 if old_parsed.hostname != new_parsed.hostname: 

159 return True 

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

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

162 # breaking backwards compatibility with older versions of requests 

163 # that allowed any redirects on the same host. 

164 if ( 

165 old_parsed.scheme == "http" 

166 and old_parsed.port in (80, None) 

167 and new_parsed.scheme == "https" 

168 and new_parsed.port in (443, None) 

169 ): 

170 return False 

171 

172 # Handle default port usage corresponding to scheme. 

173 changed_port = old_parsed.port != new_parsed.port 

174 changed_scheme = old_parsed.scheme != new_parsed.scheme 

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

176 if ( 

177 not changed_scheme 

178 and old_parsed.port in default_port 

179 and new_parsed.port in default_port 

180 ): 

181 return False 

182 

183 # Standard case: root URI must match 

184 return changed_port or changed_scheme 

185 

186 def resolve_redirects( 

187 self, 

188 resp: Response, 

189 req: PreparedRequest, 

190 stream: bool = False, 

191 timeout: _t.TimeoutType = None, 

192 verify: _t.VerifyType = True, 

193 cert: _t.CertType = None, 

194 proxies: dict[str, str] | None = None, 

195 yield_requests: bool = False, 

196 **adapter_kwargs: Any, 

197 ) -> Generator[Response, None, None]: 

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

199 

200 hist: list[Response] = [] # keep track of history 

201 

202 url = self.get_redirect_target(resp) 

203 previous_fragment = urlparse(req.url).fragment 

204 while url: 

205 prepared_request = req.copy() 

206 

207 # Update history and keep track of redirects. 

208 resp.history = hist[:] 

209 hist.append(resp) 

210 

211 try: 

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

213 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): 

214 resp.raw.read(decode_content=False) 

215 

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

217 raise TooManyRedirects( 

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

219 ) 

220 

221 # Release the connection back into the pool. 

222 resp.close() 

223 

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

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

226 parsed_rurl = urlparse(resp.url) 

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

228 

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

230 parsed = urlparse(url) 

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

232 parsed = parsed._replace(fragment=previous_fragment) 

233 elif parsed.fragment: 

234 previous_fragment = parsed.fragment 

235 url = parsed.geturl() 

236 

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

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

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

240 if not parsed.netloc: 

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

242 else: 

243 url = requote_uri(url) 

244 

245 prepared_request.url = to_native_string(url) 

246 

247 self.rebuild_method(prepared_request, resp) 

248 

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

250 if resp.status_code not in ( 

251 codes.temporary_redirect, 

252 codes.permanent_redirect, 

253 ): 

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

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

256 for header in purged_headers: 

257 prepared_request.headers.pop(header, None) 

258 prepared_request.body = None 

259 

260 headers = prepared_request.headers 

261 headers.pop("Cookie", None) 

262 

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

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

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

266 cookie_jar = cast("CookieJar", prepared_request._cookies) # type: ignore[reportPrivateUsage] 

267 extract_cookies_to_jar(cookie_jar, req, resp.raw) 

268 merge_cookies(cookie_jar, self.cookies) 

269 prepared_request.prepare_cookies(cookie_jar) 

270 

271 # Rebuild auth and proxy information. 

272 proxies = self.rebuild_proxies(prepared_request, proxies) 

273 self.rebuild_auth(prepared_request, resp) 

274 

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

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

277 # UnrewindableBodyError, instead of hanging the connection. 

278 rewindable = prepared_request._body_position is not None and ( # type: ignore[reportPrivateUsage] 

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

280 ) 

281 

282 # Attempt to rewind consumed file-like object. 

283 if rewindable: 

284 rewind_body(prepared_request) 

285 

286 # Override the original request. 

287 req = prepared_request 

288 

289 if yield_requests: 

290 yield req # type: ignore[misc] # Internal use only, returns PreparedRequest 

291 else: 

292 resp = self.send( 

293 req, 

294 stream=stream, 

295 timeout=timeout, 

296 verify=verify, 

297 cert=cert, 

298 proxies=proxies, 

299 allow_redirects=False, 

300 **adapter_kwargs, 

301 ) 

302 

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

304 

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

306 url = self.get_redirect_target(resp) 

307 yield resp 

308 

309 def rebuild_auth( 

310 self, prepared_request: PreparedRequest, response: Response 

311 ) -> None: 

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

313 request to avoid leaking credentials. This method intelligently removes 

314 and reapplies authentication where possible to avoid credential loss. 

315 """ 

316 original_request = response.request 

317 assert _is_prepared(original_request) 

318 assert _is_prepared(prepared_request) 

319 

320 headers = prepared_request.headers 

321 original_url = original_request.url 

322 url = prepared_request.url 

323 

324 if "Authorization" in headers and self.should_strip_auth(original_url, url): 

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

326 # authentication headers. 

327 del headers["Authorization"] 

328 

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

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

331 if new_auth is not None: 

332 prepared_request.prepare_auth(new_auth) 

333 

334 def rebuild_proxies( 

335 self, 

336 prepared_request: PreparedRequest, 

337 proxies: dict[str, str] | None, 

338 ) -> dict[str, str]: 

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

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

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

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

343 redirect). 

344 

345 This method also replaces the Proxy-Authorization header where 

346 necessary. 

347 

348 :rtype: dict 

349 """ 

350 assert _is_prepared(prepared_request) 

351 headers = prepared_request.headers 

352 scheme = urlparse(prepared_request.url).scheme 

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

354 

355 if "Proxy-Authorization" in headers: 

356 del headers["Proxy-Authorization"] 

357 

358 try: 

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

360 except KeyError: 

361 username, password = None, None 

362 

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

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

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

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

367 

368 return new_proxies 

369 

370 def rebuild_method( 

371 self, prepared_request: PreparedRequest, response: Response 

372 ) -> None: 

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

374 based on certain specs or browser behavior. 

375 """ 

376 method = prepared_request.method 

377 

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

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

380 method = "GET" 

381 

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

383 # First, turn 302s into GETs. 

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

385 method = "GET" 

386 

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

388 # This bizarre behaviour is explained in Issue 1704. 

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

390 method = "GET" 

391 

392 prepared_request.method = method 

393 

394 

395class Session(SessionRedirectMixin): 

396 """A Requests session. 

397 

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

399 

400 Basic Usage:: 

401 

402 >>> import requests 

403 >>> s = requests.Session() 

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

405 <Response [200]> 

406 

407 Or as a context manager:: 

408 

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

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

411 <Response [200]> 

412 """ 

413 

414 headers: CaseInsensitiveDict[str] 

415 auth: _t.AuthType 

416 proxies: dict[str, str] 

417 hooks: dict[str, list[_t.HookType]] 

418 params: MutableMapping[str, Any] 

419 stream: bool 

420 verify: _t.VerifyType 

421 cert: _t.CertType 

422 max_redirects: int 

423 trust_env: bool 

424 cookies: RequestsCookieJar 

425 adapters: MutableMapping[str, BaseAdapter] 

426 

427 __attrs__: list[str] = [ 

428 "headers", 

429 "cookies", 

430 "auth", 

431 "proxies", 

432 "hooks", 

433 "params", 

434 "verify", 

435 "cert", 

436 "adapters", 

437 "stream", 

438 "trust_env", 

439 "max_redirects", 

440 ] 

441 

442 def __init__(self) -> None: 

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

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

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

446 self.headers = default_headers() 

447 

448 #: Default Authentication tuple or object to attach to 

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

450 self.auth = None 

451 

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

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

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

455 self.proxies = {} 

456 

457 #: Event-handling hooks. 

458 self.hooks = default_hooks() 

459 

460 #: Dictionary of querystring data to attach to each 

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

462 #: representing multivalued query parameters. 

463 self.params = {} 

464 

465 #: Stream response content default. 

466 self.stream = False 

467 

468 #: SSL Verification default. 

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

470 #: remote end. 

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

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

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

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

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

476 #: If verify is set to a string, it must be the path to a CA bundle file 

477 #: that will be used to verify the TLS certificate. 

478 self.verify = True 

479 

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

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

482 self.cert = None 

483 

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

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

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

487 #: 30. 

488 self.max_redirects = DEFAULT_REDIRECT_LIMIT 

489 

490 #: Trust environment settings for proxy configuration, default 

491 #: authentication and similar. 

492 self.trust_env = True 

493 

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

495 #: session. By default it is a 

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

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

498 self.cookies = cookiejar_from_dict({}) 

499 

500 # Default connection adapters. 

501 self.adapters = OrderedDict() 

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

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

504 

505 def __enter__(self) -> Self: 

506 return self 

507 

508 def __exit__(self, *args: Any) -> None: 

509 self.close() 

510 

511 def prepare_request(self, request: Request) -> PreparedRequest: 

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

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

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

515 :class:`Session`. 

516 

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

518 session's settings. 

519 :rtype: requests.PreparedRequest 

520 """ 

521 url = cast("_t.UriType", request.url) 

522 method = cast(str, request.method) 

523 

524 cookies = request.cookies or {} 

525 

526 # Bootstrap CookieJar. 

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

528 cookies = cookiejar_from_dict(cookies) 

529 

530 # Merge with session cookies 

531 merged_cookies = merge_cookies( 

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

533 ) 

534 

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

536 auth = request.auth 

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

538 auth = get_netrc_auth(url) 

539 

540 p = PreparedRequest() 

541 p.prepare( 

542 method=method.upper(), 

543 url=url, 

544 files=request.files, 

545 data=request.data, 

546 json=request.json, 

547 headers=merge_setting( 

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

549 ), 

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

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

552 cookies=merged_cookies, 

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

554 ) 

555 return p 

556 

557 def request( 

558 self, 

559 method: str, 

560 url: _t.UriType, 

561 params: _t.ParamsType = None, 

562 data: _t.DataType = None, 

563 headers: _t.HeadersType = None, 

564 cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None, 

565 files: _t.FilesType = None, 

566 auth: _t.AuthType = None, 

567 timeout: _t.TimeoutType = None, 

568 allow_redirects: bool = True, 

569 proxies: dict[str, str] | None = None, 

570 hooks: _t.HooksInputType | None = None, 

571 stream: bool | None = None, 

572 verify: _t.VerifyType | None = None, 

573 cert: _t.CertType = None, 

574 json: _t.JsonType = None, 

575 ) -> Response: 

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

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

578 

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

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

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

582 string for the :class:`Request`. 

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

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

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

586 :class:`Request`. 

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

588 :class:`Request`. 

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

590 :class:`Request`. 

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

592 for multipart encoding upload. 

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

594 Basic/Digest/Custom HTTP Auth. 

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

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

597 read timeout) <timeouts>` tuple. 

598 :type timeout: float or tuple 

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

600 :type allow_redirects: bool 

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

602 hostname to the URL of the proxy. 

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

604 list of events, event must be callable. 

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

606 content. Defaults to ``False``. 

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

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

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

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

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

612 certificates, which will make your application vulnerable to 

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

614 may be useful during local development or testing. 

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

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

617 :rtype: requests.Response 

618 """ 

619 if isinstance(url, bytes): 

620 url = url.decode("utf-8") 

621 

622 # Create the Request. 

623 req = Request( 

624 method=method.upper(), 

625 url=url, 

626 headers=headers, 

627 files=files, 

628 data=data or {}, 

629 json=json, 

630 params=params or {}, 

631 auth=auth, 

632 cookies=cookies, 

633 hooks=hooks, 

634 ) 

635 prep = self.prepare_request(req) 

636 

637 assert _is_prepared(prep) 

638 

639 proxies = proxies or {} 

640 

641 settings = self.merge_environment_settings( 

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

643 ) 

644 

645 # Send the request. 

646 send_kwargs = { 

647 "timeout": timeout, 

648 "allow_redirects": allow_redirects, 

649 } 

650 send_kwargs.update(settings) 

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

652 

653 return resp 

654 

655 def get( 

656 self, 

657 url: _t.UriType, 

658 params: _t.ParamsType = None, 

659 **kwargs: Unpack[_t.GetKwargs], 

660 ) -> Response: 

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

662 

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

664 :param params: (optional) Dictionary, list of tuples or bytes to send 

665 in the query string for the :class:`Request`. 

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

667 :rtype: requests.Response 

668 """ 

669 

670 kwargs.setdefault("allow_redirects", True) 

671 return self.request("GET", url, params=params, **kwargs) 

672 

673 def options(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: 

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

675 

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

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

678 :rtype: requests.Response 

679 """ 

680 

681 kwargs.setdefault("allow_redirects", True) 

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

683 

684 def head(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: 

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

686 

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

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

689 :rtype: requests.Response 

690 """ 

691 

692 kwargs.setdefault("allow_redirects", False) 

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

694 

695 def post( 

696 self, 

697 url: _t.UriType, 

698 data: _t.DataType = None, 

699 json: _t.JsonType = None, 

700 **kwargs: Unpack[_t.PostKwargs], 

701 ) -> Response: 

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

703 

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

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

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

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

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

709 :rtype: requests.Response 

710 """ 

711 

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

713 

714 def put( 

715 self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] 

716 ) -> Response: 

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

718 

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

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

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

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

723 :rtype: requests.Response 

724 """ 

725 

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

727 

728 def patch( 

729 self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs] 

730 ) -> Response: 

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

732 

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

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

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

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

737 :rtype: requests.Response 

738 """ 

739 

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

741 

742 def delete(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response: 

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

744 

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

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

747 :rtype: requests.Response 

748 """ 

749 

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

751 

752 def send(self, request: PreparedRequest, **kwargs: Any) -> Response: 

753 """Send a given PreparedRequest. 

754 

755 :rtype: requests.Response 

756 """ 

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

758 # the correct parameters to reproduce the previous request. 

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

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

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

762 if "proxies" not in kwargs: 

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

764 

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

766 # Guard against that specific failure case. 

767 if isinstance(request, Request): 

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

769 

770 assert _is_prepared(request) 

771 

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

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

774 stream = kwargs.get("stream") 

775 hooks = request.hooks 

776 

777 # Get the appropriate adapter to use 

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

779 

780 # Start time (approximately) of the request 

781 start = preferred_clock() 

782 

783 # Send the request 

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

785 

786 # Total elapsed time of the request (approximately) 

787 elapsed = preferred_clock() - start 

788 r.elapsed = timedelta(seconds=elapsed) 

789 

790 # Response manipulation hooks 

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

792 

793 # Persist cookies 

794 if r.history: 

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

796 for resp in r.history: 

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

798 

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

800 

801 # Resolve redirects if allowed. 

802 if allow_redirects: 

803 # Redirect resolving generator. 

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

805 history = [resp for resp in gen] 

806 else: 

807 history = [] 

808 

809 # Shuffle things around if there's history. 

810 if history: 

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

812 history.insert(0, r) 

813 # Get the last request made 

814 r = history.pop() 

815 r.history = history 

816 

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

818 if not allow_redirects: 

819 try: 

820 r._next = next( # type: ignore[assignment] # yield_requests=True returns PreparedRequest 

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

822 ) 

823 except StopIteration: 

824 pass 

825 

826 if not stream: 

827 r.content 

828 

829 return r 

830 

831 def merge_environment_settings( 

832 self, 

833 url: str, 

834 proxies: dict[str, str] | None, 

835 stream: bool | None, 

836 verify: _t.VerifyType | None, 

837 cert: _t.CertType, 

838 ) -> dict[str, Any]: 

839 """ 

840 Check the environment and merge it with some settings. 

841 

842 :rtype: dict 

843 """ 

844 # Gather clues from the surrounding environment. 

845 if self.trust_env: 

846 # Set environment's proxies. 

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

848 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) 

849 if proxies is not None: 

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

851 proxies.setdefault(k, v) 

852 

853 # Look for requests environment configuration 

854 # and be compatible with cURL. 

855 if verify is True or verify is None: 

856 verify = ( 

857 os.environ.get("REQUESTS_CA_BUNDLE") 

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

859 or verify 

860 ) 

861 

862 # Merge all the kwargs. 

863 proxies = merge_setting(proxies, self.proxies) 

864 stream = merge_setting(stream, self.stream) 

865 verify = merge_setting(verify, self.verify) 

866 cert = merge_setting(cert, self.cert) 

867 

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

869 

870 def get_adapter(self, url: str) -> BaseAdapter: 

871 """ 

872 Returns the appropriate connection adapter for the given URL. 

873 

874 :rtype: requests.adapters.BaseAdapter 

875 """ 

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

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

878 return adapter 

879 

880 # Nothing matches :-/ 

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

882 

883 def close(self) -> None: 

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

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

886 v.close() 

887 

888 def mount(self, prefix: str, adapter: BaseAdapter) -> None: 

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

890 

891 Adapters are sorted in descending order by prefix length. 

892 """ 

893 self.adapters[prefix] = adapter 

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

895 

896 for key in keys_to_move: 

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

898 

899 def __getstate__(self) -> dict[str, Any]: 

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

901 return state 

902 

903 def __setstate__(self, state: dict[str, Any]) -> None: 

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

905 setattr(self, attr, value) 

906 

907 

908def session() -> Session: 

909 """ 

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

911 

912 .. deprecated:: 1.0.0 

913 

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

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

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

917 

918 :rtype: Session 

919 """ 

920 return Session()