Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/aiohttp/client.py: 13%

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

575 statements  

1"""HTTP Client for asyncio.""" 

2 

3import asyncio 

4import base64 

5import hashlib 

6import json 

7import os 

8import sys 

9import traceback 

10import warnings 

11from contextlib import suppress 

12from types import TracebackType 

13from typing import ( 

14 TYPE_CHECKING, 

15 Any, 

16 Awaitable, 

17 Callable, 

18 Coroutine, 

19 Final, 

20 FrozenSet, 

21 Generator, 

22 Generic, 

23 Iterable, 

24 List, 

25 Mapping, 

26 Optional, 

27 Set, 

28 Tuple, 

29 Type, 

30 TypedDict, 

31 TypeVar, 

32 Union, 

33) 

34 

35import attr 

36from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

37from yarl import URL 

38 

39from . import hdrs, http, payload 

40from ._websocket.reader import WebSocketDataQueue 

41from .abc import AbstractCookieJar 

42from .client_exceptions import ( 

43 ClientConnectionError, 

44 ClientConnectionResetError, 

45 ClientConnectorCertificateError, 

46 ClientConnectorDNSError, 

47 ClientConnectorError, 

48 ClientConnectorSSLError, 

49 ClientError, 

50 ClientHttpProxyError, 

51 ClientOSError, 

52 ClientPayloadError, 

53 ClientProxyConnectionError, 

54 ClientResponseError, 

55 ClientSSLError, 

56 ConnectionTimeoutError, 

57 ContentTypeError, 

58 InvalidURL, 

59 InvalidUrlClientError, 

60 InvalidUrlRedirectClientError, 

61 NonHttpUrlClientError, 

62 NonHttpUrlRedirectClientError, 

63 RedirectClientError, 

64 ServerConnectionError, 

65 ServerDisconnectedError, 

66 ServerFingerprintMismatch, 

67 ServerTimeoutError, 

68 SocketTimeoutError, 

69 TooManyRedirects, 

70 WSMessageTypeError, 

71 WSServerHandshakeError, 

72) 

73from .client_reqrep import ( 

74 ClientRequest as ClientRequest, 

75 ClientResponse as ClientResponse, 

76 Fingerprint as Fingerprint, 

77 RequestInfo as RequestInfo, 

78 _merge_ssl_params, 

79) 

80from .client_ws import ( 

81 DEFAULT_WS_CLIENT_TIMEOUT, 

82 ClientWebSocketResponse as ClientWebSocketResponse, 

83 ClientWSTimeout as ClientWSTimeout, 

84) 

85from .connector import ( 

86 HTTP_AND_EMPTY_SCHEMA_SET, 

87 BaseConnector as BaseConnector, 

88 NamedPipeConnector as NamedPipeConnector, 

89 TCPConnector as TCPConnector, 

90 UnixConnector as UnixConnector, 

91) 

92from .cookiejar import CookieJar 

93from .helpers import ( 

94 _SENTINEL, 

95 DEBUG, 

96 EMPTY_BODY_METHODS, 

97 BasicAuth, 

98 TimeoutHandle, 

99 get_env_proxy_for_url, 

100 sentinel, 

101 strip_auth_from_url, 

102) 

103from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter 

104from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

105from .tracing import Trace, TraceConfig 

106from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL 

107 

108__all__ = ( 

109 # client_exceptions 

110 "ClientConnectionError", 

111 "ClientConnectionResetError", 

112 "ClientConnectorCertificateError", 

113 "ClientConnectorDNSError", 

114 "ClientConnectorError", 

115 "ClientConnectorSSLError", 

116 "ClientError", 

117 "ClientHttpProxyError", 

118 "ClientOSError", 

119 "ClientPayloadError", 

120 "ClientProxyConnectionError", 

121 "ClientResponseError", 

122 "ClientSSLError", 

123 "ConnectionTimeoutError", 

124 "ContentTypeError", 

125 "InvalidURL", 

126 "InvalidUrlClientError", 

127 "RedirectClientError", 

128 "NonHttpUrlClientError", 

129 "InvalidUrlRedirectClientError", 

130 "NonHttpUrlRedirectClientError", 

131 "ServerConnectionError", 

132 "ServerDisconnectedError", 

133 "ServerFingerprintMismatch", 

134 "ServerTimeoutError", 

135 "SocketTimeoutError", 

136 "TooManyRedirects", 

137 "WSServerHandshakeError", 

138 # client_reqrep 

139 "ClientRequest", 

140 "ClientResponse", 

141 "Fingerprint", 

142 "RequestInfo", 

143 # connector 

144 "BaseConnector", 

145 "TCPConnector", 

146 "UnixConnector", 

147 "NamedPipeConnector", 

148 # client_ws 

149 "ClientWebSocketResponse", 

150 # client 

151 "ClientSession", 

152 "ClientTimeout", 

153 "ClientWSTimeout", 

154 "request", 

155 "WSMessageTypeError", 

156) 

157 

158 

159if TYPE_CHECKING: 

160 from ssl import SSLContext 

161else: 

162 SSLContext = None 

163 

164if sys.version_info >= (3, 11) and TYPE_CHECKING: 

165 from typing import Unpack 

166 

167 

168class _RequestOptions(TypedDict, total=False): 

169 params: Query 

170 data: Any 

171 json: Any 

172 cookies: Union[LooseCookies, None] 

173 headers: Union[LooseHeaders, None] 

174 skip_auto_headers: Union[Iterable[str], None] 

175 auth: Union[BasicAuth, None] 

176 allow_redirects: bool 

177 max_redirects: int 

178 compress: Union[str, bool, None] 

179 chunked: Union[bool, None] 

180 expect100: bool 

181 raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] 

182 read_until_eof: bool 

183 proxy: Union[StrOrURL, None] 

184 proxy_auth: Union[BasicAuth, None] 

185 timeout: "Union[ClientTimeout, _SENTINEL, None]" 

186 ssl: Union[SSLContext, bool, Fingerprint] 

187 server_hostname: Union[str, None] 

188 proxy_headers: Union[LooseHeaders, None] 

189 trace_request_ctx: Union[Mapping[str, Any], None] 

190 read_bufsize: Union[int, None] 

191 auto_decompress: Union[bool, None] 

192 max_line_size: Union[int, None] 

193 max_field_size: Union[int, None] 

194 

195 

196@attr.s(auto_attribs=True, frozen=True, slots=True) 

197class ClientTimeout: 

198 total: Optional[float] = None 

199 connect: Optional[float] = None 

200 sock_read: Optional[float] = None 

201 sock_connect: Optional[float] = None 

202 ceil_threshold: float = 5 

203 

204 # pool_queue_timeout: Optional[float] = None 

205 # dns_resolution_timeout: Optional[float] = None 

206 # socket_connect_timeout: Optional[float] = None 

207 # connection_acquiring_timeout: Optional[float] = None 

208 # new_connection_timeout: Optional[float] = None 

209 # http_header_timeout: Optional[float] = None 

210 # response_body_timeout: Optional[float] = None 

211 

212 # to create a timeout specific for a single request, either 

213 # - create a completely new one to overwrite the default 

214 # - or use http://www.attrs.org/en/stable/api.html#attr.evolve 

215 # to overwrite the defaults 

216 

217 

218# 5 Minute default read timeout 

219DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30) 

220 

221# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 

222IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) 

223 

224_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) 

225_CharsetResolver = Callable[[ClientResponse, bytes], str] 

226 

227 

228class ClientSession: 

229 """First-class interface for making HTTP requests.""" 

230 

231 ATTRS = frozenset( 

232 [ 

233 "_base_url", 

234 "_base_url_origin", 

235 "_source_traceback", 

236 "_connector", 

237 "_loop", 

238 "_cookie_jar", 

239 "_connector_owner", 

240 "_default_auth", 

241 "_version", 

242 "_json_serialize", 

243 "_requote_redirect_url", 

244 "_timeout", 

245 "_raise_for_status", 

246 "_auto_decompress", 

247 "_trust_env", 

248 "_default_headers", 

249 "_skip_auto_headers", 

250 "_request_class", 

251 "_response_class", 

252 "_ws_response_class", 

253 "_trace_configs", 

254 "_read_bufsize", 

255 "_max_line_size", 

256 "_max_field_size", 

257 "_resolve_charset", 

258 "_default_proxy", 

259 "_default_proxy_auth", 

260 "_retry_connection", 

261 "requote_redirect_url", 

262 ] 

263 ) 

264 

265 _source_traceback: Optional[traceback.StackSummary] = None 

266 _connector: Optional[BaseConnector] = None 

267 

268 def __init__( 

269 self, 

270 base_url: Optional[StrOrURL] = None, 

271 *, 

272 connector: Optional[BaseConnector] = None, 

273 loop: Optional[asyncio.AbstractEventLoop] = None, 

274 cookies: Optional[LooseCookies] = None, 

275 headers: Optional[LooseHeaders] = None, 

276 proxy: Optional[StrOrURL] = None, 

277 proxy_auth: Optional[BasicAuth] = None, 

278 skip_auto_headers: Optional[Iterable[str]] = None, 

279 auth: Optional[BasicAuth] = None, 

280 json_serialize: JSONEncoder = json.dumps, 

281 request_class: Type[ClientRequest] = ClientRequest, 

282 response_class: Type[ClientResponse] = ClientResponse, 

283 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

284 version: HttpVersion = http.HttpVersion11, 

285 cookie_jar: Optional[AbstractCookieJar] = None, 

286 connector_owner: bool = True, 

287 raise_for_status: Union[ 

288 bool, Callable[[ClientResponse], Awaitable[None]] 

289 ] = False, 

290 read_timeout: Union[float, _SENTINEL] = sentinel, 

291 conn_timeout: Optional[float] = None, 

292 timeout: Union[object, ClientTimeout] = sentinel, 

293 auto_decompress: bool = True, 

294 trust_env: bool = False, 

295 requote_redirect_url: bool = True, 

296 trace_configs: Optional[List[TraceConfig]] = None, 

297 read_bufsize: int = 2**16, 

298 max_line_size: int = 8190, 

299 max_field_size: int = 8190, 

300 fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", 

301 ) -> None: 

302 # We initialise _connector to None immediately, as it's referenced in __del__() 

303 # and could cause issues if an exception occurs during initialisation. 

304 self._connector: Optional[BaseConnector] = None 

305 

306 if loop is None: 

307 if connector is not None: 

308 loop = connector._loop 

309 

310 loop = loop or asyncio.get_running_loop() 

311 

312 if base_url is None or isinstance(base_url, URL): 

313 self._base_url: Optional[URL] = base_url 

314 self._base_url_origin = None if base_url is None else base_url.origin() 

315 else: 

316 self._base_url = URL(base_url) 

317 self._base_url_origin = self._base_url.origin() 

318 assert self._base_url.absolute, "Only absolute URLs are supported" 

319 if self._base_url is not None and not self._base_url.path.endswith("/"): 

320 raise ValueError("base_url must have a trailing '/'") 

321 

322 if timeout is sentinel or timeout is None: 

323 self._timeout = DEFAULT_TIMEOUT 

324 if read_timeout is not sentinel: 

325 warnings.warn( 

326 "read_timeout is deprecated, use timeout argument instead", 

327 DeprecationWarning, 

328 stacklevel=2, 

329 ) 

330 self._timeout = attr.evolve(self._timeout, total=read_timeout) 

331 if conn_timeout is not None: 

332 self._timeout = attr.evolve(self._timeout, connect=conn_timeout) 

333 warnings.warn( 

334 "conn_timeout is deprecated, use timeout argument instead", 

335 DeprecationWarning, 

336 stacklevel=2, 

337 ) 

338 else: 

339 if not isinstance(timeout, ClientTimeout): 

340 raise ValueError( 

341 f"timeout parameter cannot be of {type(timeout)} type, " 

342 "please use 'timeout=ClientTimeout(...)'", 

343 ) 

344 self._timeout = timeout 

345 if read_timeout is not sentinel: 

346 raise ValueError( 

347 "read_timeout and timeout parameters " 

348 "conflict, please setup " 

349 "timeout.read" 

350 ) 

351 if conn_timeout is not None: 

352 raise ValueError( 

353 "conn_timeout and timeout parameters " 

354 "conflict, please setup " 

355 "timeout.connect" 

356 ) 

357 

358 if connector is None: 

359 connector = TCPConnector(loop=loop) 

360 

361 if connector._loop is not loop: 

362 raise RuntimeError("Session and connector has to use same event loop") 

363 

364 self._loop = loop 

365 

366 if loop.get_debug(): 

367 self._source_traceback = traceback.extract_stack(sys._getframe(1)) 

368 

369 if cookie_jar is None: 

370 cookie_jar = CookieJar(loop=loop) 

371 self._cookie_jar = cookie_jar 

372 

373 if cookies: 

374 self._cookie_jar.update_cookies(cookies) 

375 

376 self._connector = connector 

377 self._connector_owner = connector_owner 

378 self._default_auth = auth 

379 self._version = version 

380 self._json_serialize = json_serialize 

381 self._raise_for_status = raise_for_status 

382 self._auto_decompress = auto_decompress 

383 self._trust_env = trust_env 

384 self._requote_redirect_url = requote_redirect_url 

385 self._read_bufsize = read_bufsize 

386 self._max_line_size = max_line_size 

387 self._max_field_size = max_field_size 

388 

389 # Convert to list of tuples 

390 if headers: 

391 real_headers: CIMultiDict[str] = CIMultiDict(headers) 

392 else: 

393 real_headers = CIMultiDict() 

394 self._default_headers: CIMultiDict[str] = real_headers 

395 if skip_auto_headers is not None: 

396 self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers) 

397 else: 

398 self._skip_auto_headers = frozenset() 

399 

400 self._request_class = request_class 

401 self._response_class = response_class 

402 self._ws_response_class = ws_response_class 

403 

404 self._trace_configs = trace_configs or [] 

405 for trace_config in self._trace_configs: 

406 trace_config.freeze() 

407 

408 self._resolve_charset = fallback_charset_resolver 

409 

410 self._default_proxy = proxy 

411 self._default_proxy_auth = proxy_auth 

412 self._retry_connection: bool = True 

413 

414 def __init_subclass__(cls: Type["ClientSession"]) -> None: 

415 warnings.warn( 

416 "Inheritance class {} from ClientSession " 

417 "is discouraged".format(cls.__name__), 

418 DeprecationWarning, 

419 stacklevel=2, 

420 ) 

421 

422 if DEBUG: 

423 

424 def __setattr__(self, name: str, val: Any) -> None: 

425 if name not in self.ATTRS: 

426 warnings.warn( 

427 "Setting custom ClientSession.{} attribute " 

428 "is discouraged".format(name), 

429 DeprecationWarning, 

430 stacklevel=2, 

431 ) 

432 super().__setattr__(name, val) 

433 

434 def __del__(self, _warnings: Any = warnings) -> None: 

435 if not self.closed: 

436 kwargs = {"source": self} 

437 _warnings.warn( 

438 f"Unclosed client session {self!r}", ResourceWarning, **kwargs 

439 ) 

440 context = {"client_session": self, "message": "Unclosed client session"} 

441 if self._source_traceback is not None: 

442 context["source_traceback"] = self._source_traceback 

443 self._loop.call_exception_handler(context) 

444 

445 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

446 

447 def request( 

448 self, 

449 method: str, 

450 url: StrOrURL, 

451 **kwargs: Unpack[_RequestOptions], 

452 ) -> "_RequestContextManager": ... 

453 

454 else: 

455 

456 def request( 

457 self, method: str, url: StrOrURL, **kwargs: Any 

458 ) -> "_RequestContextManager": 

459 """Perform HTTP request.""" 

460 return _RequestContextManager(self._request(method, url, **kwargs)) 

461 

462 def _build_url(self, str_or_url: StrOrURL) -> URL: 

463 url = URL(str_or_url) 

464 if self._base_url is None: 

465 return url 

466 else: 

467 assert not url.absolute 

468 return self._base_url.join(url) 

469 

470 async def _request( 

471 self, 

472 method: str, 

473 str_or_url: StrOrURL, 

474 *, 

475 params: Query = None, 

476 data: Any = None, 

477 json: Any = None, 

478 cookies: Optional[LooseCookies] = None, 

479 headers: Optional[LooseHeaders] = None, 

480 skip_auto_headers: Optional[Iterable[str]] = None, 

481 auth: Optional[BasicAuth] = None, 

482 allow_redirects: bool = True, 

483 max_redirects: int = 10, 

484 compress: Union[str, bool, None] = None, 

485 chunked: Optional[bool] = None, 

486 expect100: bool = False, 

487 raise_for_status: Union[ 

488 None, bool, Callable[[ClientResponse], Awaitable[None]] 

489 ] = None, 

490 read_until_eof: bool = True, 

491 proxy: Optional[StrOrURL] = None, 

492 proxy_auth: Optional[BasicAuth] = None, 

493 timeout: Union[ClientTimeout, _SENTINEL] = sentinel, 

494 verify_ssl: Optional[bool] = None, 

495 fingerprint: Optional[bytes] = None, 

496 ssl_context: Optional[SSLContext] = None, 

497 ssl: Union[SSLContext, bool, Fingerprint] = True, 

498 server_hostname: Optional[str] = None, 

499 proxy_headers: Optional[LooseHeaders] = None, 

500 trace_request_ctx: Optional[Mapping[str, Any]] = None, 

501 read_bufsize: Optional[int] = None, 

502 auto_decompress: Optional[bool] = None, 

503 max_line_size: Optional[int] = None, 

504 max_field_size: Optional[int] = None, 

505 ) -> ClientResponse: 

506 

507 # NOTE: timeout clamps existing connect and read timeouts. We cannot 

508 # set the default to None because we need to detect if the user wants 

509 # to use the existing timeouts by setting timeout to None. 

510 

511 if self.closed: 

512 raise RuntimeError("Session is closed") 

513 

514 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) 

515 

516 if data is not None and json is not None: 

517 raise ValueError( 

518 "data and json parameters can not be used at the same time" 

519 ) 

520 elif json is not None: 

521 data = payload.JsonPayload(json, dumps=self._json_serialize) 

522 

523 if not isinstance(chunked, bool) and chunked is not None: 

524 warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) 

525 

526 redirects = 0 

527 history: List[ClientResponse] = [] 

528 version = self._version 

529 params = params or {} 

530 

531 # Merge with default headers and transform to CIMultiDict 

532 headers = self._prepare_headers(headers) 

533 

534 try: 

535 url = self._build_url(str_or_url) 

536 except ValueError as e: 

537 raise InvalidUrlClientError(str_or_url) from e 

538 

539 assert self._connector is not None 

540 if url.scheme not in self._connector.allowed_protocol_schema_set: 

541 raise NonHttpUrlClientError(url) 

542 

543 skip_headers: Optional[Iterable[istr]] 

544 if skip_auto_headers is not None: 

545 skip_headers = { 

546 istr(i) for i in skip_auto_headers 

547 } | self._skip_auto_headers 

548 elif self._skip_auto_headers: 

549 skip_headers = self._skip_auto_headers 

550 else: 

551 skip_headers = None 

552 

553 if proxy is None: 

554 proxy = self._default_proxy 

555 if proxy_auth is None: 

556 proxy_auth = self._default_proxy_auth 

557 

558 if proxy is None: 

559 proxy_headers = None 

560 else: 

561 proxy_headers = self._prepare_headers(proxy_headers) 

562 try: 

563 proxy = URL(proxy) 

564 except ValueError as e: 

565 raise InvalidURL(proxy) from e 

566 

567 if timeout is sentinel: 

568 real_timeout: ClientTimeout = self._timeout 

569 else: 

570 if not isinstance(timeout, ClientTimeout): 

571 real_timeout = ClientTimeout(total=timeout) 

572 else: 

573 real_timeout = timeout 

574 # timeout is cumulative for all request operations 

575 # (request, redirects, responses, data consuming) 

576 tm = TimeoutHandle( 

577 self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold 

578 ) 

579 handle = tm.start() 

580 

581 if read_bufsize is None: 

582 read_bufsize = self._read_bufsize 

583 

584 if auto_decompress is None: 

585 auto_decompress = self._auto_decompress 

586 

587 if max_line_size is None: 

588 max_line_size = self._max_line_size 

589 

590 if max_field_size is None: 

591 max_field_size = self._max_field_size 

592 

593 traces = [ 

594 Trace( 

595 self, 

596 trace_config, 

597 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

598 ) 

599 for trace_config in self._trace_configs 

600 ] 

601 

602 for trace in traces: 

603 await trace.send_request_start(method, url.update_query(params), headers) 

604 

605 timer = tm.timer() 

606 try: 

607 with timer: 

608 # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests 

609 retry_persistent_connection = ( 

610 self._retry_connection and method in IDEMPOTENT_METHODS 

611 ) 

612 while True: 

613 url, auth_from_url = strip_auth_from_url(url) 

614 if not url.raw_host: 

615 # NOTE: Bail early, otherwise, causes `InvalidURL` through 

616 # NOTE: `self._request_class()` below. 

617 err_exc_cls = ( 

618 InvalidUrlRedirectClientError 

619 if redirects 

620 else InvalidUrlClientError 

621 ) 

622 raise err_exc_cls(url) 

623 # If `auth` was passed for an already authenticated URL, 

624 # disallow only if this is the initial URL; this is to avoid issues 

625 # with sketchy redirects that are not the caller's responsibility 

626 if not history and (auth and auth_from_url): 

627 raise ValueError( 

628 "Cannot combine AUTH argument with " 

629 "credentials encoded in URL" 

630 ) 

631 

632 # Override the auth with the one from the URL only if we 

633 # have no auth, or if we got an auth from a redirect URL 

634 if auth is None or (history and auth_from_url is not None): 

635 auth = auth_from_url 

636 

637 if ( 

638 auth is None 

639 and self._default_auth 

640 and ( 

641 not self._base_url or self._base_url_origin == url.origin() 

642 ) 

643 ): 

644 auth = self._default_auth 

645 # It would be confusing if we support explicit 

646 # Authorization header with auth argument 

647 if ( 

648 headers is not None 

649 and auth is not None 

650 and hdrs.AUTHORIZATION in headers 

651 ): 

652 raise ValueError( 

653 "Cannot combine AUTHORIZATION header " 

654 "with AUTH argument or credentials " 

655 "encoded in URL" 

656 ) 

657 

658 all_cookies = self._cookie_jar.filter_cookies(url) 

659 

660 if cookies is not None: 

661 tmp_cookie_jar = CookieJar( 

662 quote_cookie=self._cookie_jar.quote_cookie 

663 ) 

664 tmp_cookie_jar.update_cookies(cookies) 

665 req_cookies = tmp_cookie_jar.filter_cookies(url) 

666 if req_cookies: 

667 all_cookies.load(req_cookies) 

668 

669 if proxy is not None: 

670 proxy = URL(proxy) 

671 elif self._trust_env: 

672 with suppress(LookupError): 

673 proxy, proxy_auth = get_env_proxy_for_url(url) 

674 

675 req = self._request_class( 

676 method, 

677 url, 

678 params=params, 

679 headers=headers, 

680 skip_auto_headers=skip_headers, 

681 data=data, 

682 cookies=all_cookies, 

683 auth=auth, 

684 version=version, 

685 compress=compress, 

686 chunked=chunked, 

687 expect100=expect100, 

688 loop=self._loop, 

689 response_class=self._response_class, 

690 proxy=proxy, 

691 proxy_auth=proxy_auth, 

692 timer=timer, 

693 session=self, 

694 ssl=ssl if ssl is not None else True, 

695 server_hostname=server_hostname, 

696 proxy_headers=proxy_headers, 

697 traces=traces, 

698 trust_env=self.trust_env, 

699 ) 

700 

701 # connection timeout 

702 try: 

703 conn = await self._connector.connect( 

704 req, traces=traces, timeout=real_timeout 

705 ) 

706 except asyncio.TimeoutError as exc: 

707 raise ConnectionTimeoutError( 

708 f"Connection timeout to host {url}" 

709 ) from exc 

710 

711 assert conn.transport is not None 

712 

713 assert conn.protocol is not None 

714 conn.protocol.set_response_params( 

715 timer=timer, 

716 skip_payload=method in EMPTY_BODY_METHODS, 

717 read_until_eof=read_until_eof, 

718 auto_decompress=auto_decompress, 

719 read_timeout=real_timeout.sock_read, 

720 read_bufsize=read_bufsize, 

721 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

722 max_line_size=max_line_size, 

723 max_field_size=max_field_size, 

724 ) 

725 

726 try: 

727 try: 

728 resp = await req.send(conn) 

729 try: 

730 await resp.start(conn) 

731 except BaseException: 

732 resp.close() 

733 raise 

734 except BaseException: 

735 conn.close() 

736 raise 

737 except (ClientOSError, ServerDisconnectedError): 

738 if retry_persistent_connection: 

739 retry_persistent_connection = False 

740 continue 

741 raise 

742 except ClientError: 

743 raise 

744 except OSError as exc: 

745 if exc.errno is None and isinstance(exc, asyncio.TimeoutError): 

746 raise 

747 raise ClientOSError(*exc.args) from exc 

748 

749 if cookies := resp._cookies: 

750 self._cookie_jar.update_cookies(cookies, resp.url) 

751 

752 # redirects 

753 if resp.status in (301, 302, 303, 307, 308) and allow_redirects: 

754 

755 for trace in traces: 

756 await trace.send_request_redirect( 

757 method, url.update_query(params), headers, resp 

758 ) 

759 

760 redirects += 1 

761 history.append(resp) 

762 if max_redirects and redirects >= max_redirects: 

763 resp.close() 

764 raise TooManyRedirects( 

765 history[0].request_info, tuple(history) 

766 ) 

767 

768 # For 301 and 302, mimic IE, now changed in RFC 

769 # https://github.com/kennethreitz/requests/pull/269 

770 if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or ( 

771 resp.status in (301, 302) and resp.method == hdrs.METH_POST 

772 ): 

773 method = hdrs.METH_GET 

774 data = None 

775 if headers.get(hdrs.CONTENT_LENGTH): 

776 headers.pop(hdrs.CONTENT_LENGTH) 

777 

778 r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get( 

779 hdrs.URI 

780 ) 

781 if r_url is None: 

782 # see github.com/aio-libs/aiohttp/issues/2022 

783 break 

784 else: 

785 # reading from correct redirection 

786 # response is forbidden 

787 resp.release() 

788 

789 try: 

790 parsed_redirect_url = URL( 

791 r_url, encoded=not self._requote_redirect_url 

792 ) 

793 except ValueError as e: 

794 raise InvalidUrlRedirectClientError( 

795 r_url, 

796 "Server attempted redirecting to a location that does not look like a URL", 

797 ) from e 

798 

799 scheme = parsed_redirect_url.scheme 

800 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

801 resp.close() 

802 raise NonHttpUrlRedirectClientError(r_url) 

803 elif not scheme: 

804 parsed_redirect_url = url.join(parsed_redirect_url) 

805 

806 try: 

807 redirect_origin = parsed_redirect_url.origin() 

808 except ValueError as origin_val_err: 

809 raise InvalidUrlRedirectClientError( 

810 parsed_redirect_url, 

811 "Invalid redirect URL origin", 

812 ) from origin_val_err 

813 

814 if url.origin() != redirect_origin: 

815 auth = None 

816 headers.pop(hdrs.AUTHORIZATION, None) 

817 

818 url = parsed_redirect_url 

819 params = {} 

820 resp.release() 

821 continue 

822 

823 break 

824 

825 # check response status 

826 if raise_for_status is None: 

827 raise_for_status = self._raise_for_status 

828 

829 if raise_for_status is None: 

830 pass 

831 elif callable(raise_for_status): 

832 await raise_for_status(resp) 

833 elif raise_for_status: 

834 resp.raise_for_status() 

835 

836 # register connection 

837 if handle is not None: 

838 if resp.connection is not None: 

839 resp.connection.add_callback(handle.cancel) 

840 else: 

841 handle.cancel() 

842 

843 resp._history = tuple(history) 

844 

845 for trace in traces: 

846 await trace.send_request_end( 

847 method, url.update_query(params), headers, resp 

848 ) 

849 return resp 

850 

851 except BaseException as e: 

852 # cleanup timer 

853 tm.close() 

854 if handle: 

855 handle.cancel() 

856 handle = None 

857 

858 for trace in traces: 

859 await trace.send_request_exception( 

860 method, url.update_query(params), headers, e 

861 ) 

862 raise 

863 

864 def ws_connect( 

865 self, 

866 url: StrOrURL, 

867 *, 

868 method: str = hdrs.METH_GET, 

869 protocols: Iterable[str] = (), 

870 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, 

871 receive_timeout: Optional[float] = None, 

872 autoclose: bool = True, 

873 autoping: bool = True, 

874 heartbeat: Optional[float] = None, 

875 auth: Optional[BasicAuth] = None, 

876 origin: Optional[str] = None, 

877 params: Query = None, 

878 headers: Optional[LooseHeaders] = None, 

879 proxy: Optional[StrOrURL] = None, 

880 proxy_auth: Optional[BasicAuth] = None, 

881 ssl: Union[SSLContext, bool, Fingerprint] = True, 

882 verify_ssl: Optional[bool] = None, 

883 fingerprint: Optional[bytes] = None, 

884 ssl_context: Optional[SSLContext] = None, 

885 server_hostname: Optional[str] = None, 

886 proxy_headers: Optional[LooseHeaders] = None, 

887 compress: int = 0, 

888 max_msg_size: int = 4 * 1024 * 1024, 

889 ) -> "_WSRequestContextManager": 

890 """Initiate websocket connection.""" 

891 return _WSRequestContextManager( 

892 self._ws_connect( 

893 url, 

894 method=method, 

895 protocols=protocols, 

896 timeout=timeout, 

897 receive_timeout=receive_timeout, 

898 autoclose=autoclose, 

899 autoping=autoping, 

900 heartbeat=heartbeat, 

901 auth=auth, 

902 origin=origin, 

903 params=params, 

904 headers=headers, 

905 proxy=proxy, 

906 proxy_auth=proxy_auth, 

907 ssl=ssl, 

908 verify_ssl=verify_ssl, 

909 fingerprint=fingerprint, 

910 ssl_context=ssl_context, 

911 server_hostname=server_hostname, 

912 proxy_headers=proxy_headers, 

913 compress=compress, 

914 max_msg_size=max_msg_size, 

915 ) 

916 ) 

917 

918 async def _ws_connect( 

919 self, 

920 url: StrOrURL, 

921 *, 

922 method: str = hdrs.METH_GET, 

923 protocols: Iterable[str] = (), 

924 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, 

925 receive_timeout: Optional[float] = None, 

926 autoclose: bool = True, 

927 autoping: bool = True, 

928 heartbeat: Optional[float] = None, 

929 auth: Optional[BasicAuth] = None, 

930 origin: Optional[str] = None, 

931 params: Query = None, 

932 headers: Optional[LooseHeaders] = None, 

933 proxy: Optional[StrOrURL] = None, 

934 proxy_auth: Optional[BasicAuth] = None, 

935 ssl: Union[SSLContext, bool, Fingerprint] = True, 

936 verify_ssl: Optional[bool] = None, 

937 fingerprint: Optional[bytes] = None, 

938 ssl_context: Optional[SSLContext] = None, 

939 server_hostname: Optional[str] = None, 

940 proxy_headers: Optional[LooseHeaders] = None, 

941 compress: int = 0, 

942 max_msg_size: int = 4 * 1024 * 1024, 

943 ) -> ClientWebSocketResponse: 

944 if timeout is not sentinel: 

945 if isinstance(timeout, ClientWSTimeout): 

946 ws_timeout = timeout 

947 else: 

948 warnings.warn( 

949 "parameter 'timeout' of type 'float' " 

950 "is deprecated, please use " 

951 "'timeout=ClientWSTimeout(ws_close=...)'", 

952 DeprecationWarning, 

953 stacklevel=2, 

954 ) 

955 ws_timeout = ClientWSTimeout(ws_close=timeout) 

956 else: 

957 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

958 if receive_timeout is not None: 

959 warnings.warn( 

960 "float parameter 'receive_timeout' " 

961 "is deprecated, please use parameter " 

962 "'timeout=ClientWSTimeout(ws_receive=...)'", 

963 DeprecationWarning, 

964 stacklevel=2, 

965 ) 

966 ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout) 

967 

968 if headers is None: 

969 real_headers: CIMultiDict[str] = CIMultiDict() 

970 else: 

971 real_headers = CIMultiDict(headers) 

972 

973 default_headers = { 

974 hdrs.UPGRADE: "websocket", 

975 hdrs.CONNECTION: "Upgrade", 

976 hdrs.SEC_WEBSOCKET_VERSION: "13", 

977 } 

978 

979 for key, value in default_headers.items(): 

980 real_headers.setdefault(key, value) 

981 

982 sec_key = base64.b64encode(os.urandom(16)) 

983 real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode() 

984 

985 if protocols: 

986 real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols) 

987 if origin is not None: 

988 real_headers[hdrs.ORIGIN] = origin 

989 if compress: 

990 extstr = ws_ext_gen(compress=compress) 

991 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

992 

993 # For the sake of backward compatibility, if user passes in None, convert it to True 

994 if ssl is None: 

995 warnings.warn( 

996 "ssl=None is deprecated, please use ssl=True", 

997 DeprecationWarning, 

998 stacklevel=2, 

999 ) 

1000 ssl = True 

1001 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) 

1002 

1003 # send request 

1004 resp = await self.request( 

1005 method, 

1006 url, 

1007 params=params, 

1008 headers=real_headers, 

1009 read_until_eof=False, 

1010 auth=auth, 

1011 proxy=proxy, 

1012 proxy_auth=proxy_auth, 

1013 ssl=ssl, 

1014 server_hostname=server_hostname, 

1015 proxy_headers=proxy_headers, 

1016 ) 

1017 

1018 try: 

1019 # check handshake 

1020 if resp.status != 101: 

1021 raise WSServerHandshakeError( 

1022 resp.request_info, 

1023 resp.history, 

1024 message="Invalid response status", 

1025 status=resp.status, 

1026 headers=resp.headers, 

1027 ) 

1028 

1029 if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket": 

1030 raise WSServerHandshakeError( 

1031 resp.request_info, 

1032 resp.history, 

1033 message="Invalid upgrade header", 

1034 status=resp.status, 

1035 headers=resp.headers, 

1036 ) 

1037 

1038 if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade": 

1039 raise WSServerHandshakeError( 

1040 resp.request_info, 

1041 resp.history, 

1042 message="Invalid connection header", 

1043 status=resp.status, 

1044 headers=resp.headers, 

1045 ) 

1046 

1047 # key calculation 

1048 r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "") 

1049 match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode() 

1050 if r_key != match: 

1051 raise WSServerHandshakeError( 

1052 resp.request_info, 

1053 resp.history, 

1054 message="Invalid challenge response", 

1055 status=resp.status, 

1056 headers=resp.headers, 

1057 ) 

1058 

1059 # websocket protocol 

1060 protocol = None 

1061 if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers: 

1062 resp_protocols = [ 

1063 proto.strip() 

1064 for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") 

1065 ] 

1066 

1067 for proto in resp_protocols: 

1068 if proto in protocols: 

1069 protocol = proto 

1070 break 

1071 

1072 # websocket compress 

1073 notakeover = False 

1074 if compress: 

1075 compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) 

1076 if compress_hdrs: 

1077 try: 

1078 compress, notakeover = ws_ext_parse(compress_hdrs) 

1079 except WSHandshakeError as exc: 

1080 raise WSServerHandshakeError( 

1081 resp.request_info, 

1082 resp.history, 

1083 message=exc.args[0], 

1084 status=resp.status, 

1085 headers=resp.headers, 

1086 ) from exc 

1087 else: 

1088 compress = 0 

1089 notakeover = False 

1090 

1091 conn = resp.connection 

1092 assert conn is not None 

1093 conn_proto = conn.protocol 

1094 assert conn_proto is not None 

1095 

1096 # For WS connection the read_timeout must be either receive_timeout or greater 

1097 # None == no timeout, i.e. infinite timeout, so None is the max timeout possible 

1098 if ws_timeout.ws_receive is None: 

1099 # Reset regardless 

1100 conn_proto.read_timeout = None 

1101 elif conn_proto.read_timeout is not None: 

1102 conn_proto.read_timeout = max( 

1103 ws_timeout.ws_receive, conn_proto.read_timeout 

1104 ) 

1105 

1106 transport = conn.transport 

1107 assert transport is not None 

1108 reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop) 

1109 conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) 

1110 writer = WebSocketWriter( 

1111 conn_proto, 

1112 transport, 

1113 use_mask=True, 

1114 compress=compress, 

1115 notakeover=notakeover, 

1116 ) 

1117 except BaseException: 

1118 resp.close() 

1119 raise 

1120 else: 

1121 return self._ws_response_class( 

1122 reader, 

1123 writer, 

1124 protocol, 

1125 resp, 

1126 ws_timeout, 

1127 autoclose, 

1128 autoping, 

1129 self._loop, 

1130 heartbeat=heartbeat, 

1131 compress=compress, 

1132 client_notakeover=notakeover, 

1133 ) 

1134 

1135 def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]": 

1136 """Add default headers and transform it to CIMultiDict""" 

1137 # Convert headers to MultiDict 

1138 result = CIMultiDict(self._default_headers) 

1139 if headers: 

1140 if not isinstance(headers, (MultiDictProxy, MultiDict)): 

1141 headers = CIMultiDict(headers) 

1142 added_names: Set[str] = set() 

1143 for key, value in headers.items(): 

1144 if key in added_names: 

1145 result.add(key, value) 

1146 else: 

1147 result[key] = value 

1148 added_names.add(key) 

1149 return result 

1150 

1151 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

1152 

1153 def get( 

1154 self, 

1155 url: StrOrURL, 

1156 **kwargs: Unpack[_RequestOptions], 

1157 ) -> "_RequestContextManager": ... 

1158 

1159 def options( 

1160 self, 

1161 url: StrOrURL, 

1162 **kwargs: Unpack[_RequestOptions], 

1163 ) -> "_RequestContextManager": ... 

1164 

1165 def head( 

1166 self, 

1167 url: StrOrURL, 

1168 **kwargs: Unpack[_RequestOptions], 

1169 ) -> "_RequestContextManager": ... 

1170 

1171 def post( 

1172 self, 

1173 url: StrOrURL, 

1174 **kwargs: Unpack[_RequestOptions], 

1175 ) -> "_RequestContextManager": ... 

1176 

1177 def put( 

1178 self, 

1179 url: StrOrURL, 

1180 **kwargs: Unpack[_RequestOptions], 

1181 ) -> "_RequestContextManager": ... 

1182 

1183 def patch( 

1184 self, 

1185 url: StrOrURL, 

1186 **kwargs: Unpack[_RequestOptions], 

1187 ) -> "_RequestContextManager": ... 

1188 

1189 def delete( 

1190 self, 

1191 url: StrOrURL, 

1192 **kwargs: Unpack[_RequestOptions], 

1193 ) -> "_RequestContextManager": ... 

1194 

1195 else: 

1196 

1197 def get( 

1198 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any 

1199 ) -> "_RequestContextManager": 

1200 """Perform HTTP GET request.""" 

1201 return _RequestContextManager( 

1202 self._request( 

1203 hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs 

1204 ) 

1205 ) 

1206 

1207 def options( 

1208 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any 

1209 ) -> "_RequestContextManager": 

1210 """Perform HTTP OPTIONS request.""" 

1211 return _RequestContextManager( 

1212 self._request( 

1213 hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs 

1214 ) 

1215 ) 

1216 

1217 def head( 

1218 self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any 

1219 ) -> "_RequestContextManager": 

1220 """Perform HTTP HEAD request.""" 

1221 return _RequestContextManager( 

1222 self._request( 

1223 hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs 

1224 ) 

1225 ) 

1226 

1227 def post( 

1228 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1229 ) -> "_RequestContextManager": 

1230 """Perform HTTP POST request.""" 

1231 return _RequestContextManager( 

1232 self._request(hdrs.METH_POST, url, data=data, **kwargs) 

1233 ) 

1234 

1235 def put( 

1236 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1237 ) -> "_RequestContextManager": 

1238 """Perform HTTP PUT request.""" 

1239 return _RequestContextManager( 

1240 self._request(hdrs.METH_PUT, url, data=data, **kwargs) 

1241 ) 

1242 

1243 def patch( 

1244 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1245 ) -> "_RequestContextManager": 

1246 """Perform HTTP PATCH request.""" 

1247 return _RequestContextManager( 

1248 self._request(hdrs.METH_PATCH, url, data=data, **kwargs) 

1249 ) 

1250 

1251 def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager": 

1252 """Perform HTTP DELETE request.""" 

1253 return _RequestContextManager( 

1254 self._request(hdrs.METH_DELETE, url, **kwargs) 

1255 ) 

1256 

1257 async def close(self) -> None: 

1258 """Close underlying connector. 

1259 

1260 Release all acquired resources. 

1261 """ 

1262 if not self.closed: 

1263 if self._connector is not None and self._connector_owner: 

1264 await self._connector.close() 

1265 self._connector = None 

1266 

1267 @property 

1268 def closed(self) -> bool: 

1269 """Is client session closed. 

1270 

1271 A readonly property. 

1272 """ 

1273 return self._connector is None or self._connector.closed 

1274 

1275 @property 

1276 def connector(self) -> Optional[BaseConnector]: 

1277 """Connector instance used for the session.""" 

1278 return self._connector 

1279 

1280 @property 

1281 def cookie_jar(self) -> AbstractCookieJar: 

1282 """The session cookies.""" 

1283 return self._cookie_jar 

1284 

1285 @property 

1286 def version(self) -> Tuple[int, int]: 

1287 """The session HTTP protocol version.""" 

1288 return self._version 

1289 

1290 @property 

1291 def requote_redirect_url(self) -> bool: 

1292 """Do URL requoting on redirection handling.""" 

1293 return self._requote_redirect_url 

1294 

1295 @requote_redirect_url.setter 

1296 def requote_redirect_url(self, val: bool) -> None: 

1297 """Do URL requoting on redirection handling.""" 

1298 warnings.warn( 

1299 "session.requote_redirect_url modification is deprecated #2778", 

1300 DeprecationWarning, 

1301 stacklevel=2, 

1302 ) 

1303 self._requote_redirect_url = val 

1304 

1305 @property 

1306 def loop(self) -> asyncio.AbstractEventLoop: 

1307 """Session's loop.""" 

1308 warnings.warn( 

1309 "client.loop property is deprecated", DeprecationWarning, stacklevel=2 

1310 ) 

1311 return self._loop 

1312 

1313 @property 

1314 def timeout(self) -> ClientTimeout: 

1315 """Timeout for the session.""" 

1316 return self._timeout 

1317 

1318 @property 

1319 def headers(self) -> "CIMultiDict[str]": 

1320 """The default headers of the client session.""" 

1321 return self._default_headers 

1322 

1323 @property 

1324 def skip_auto_headers(self) -> FrozenSet[istr]: 

1325 """Headers for which autogeneration should be skipped""" 

1326 return self._skip_auto_headers 

1327 

1328 @property 

1329 def auth(self) -> Optional[BasicAuth]: 

1330 """An object that represents HTTP Basic Authorization""" 

1331 return self._default_auth 

1332 

1333 @property 

1334 def json_serialize(self) -> JSONEncoder: 

1335 """Json serializer callable""" 

1336 return self._json_serialize 

1337 

1338 @property 

1339 def connector_owner(self) -> bool: 

1340 """Should connector be closed on session closing""" 

1341 return self._connector_owner 

1342 

1343 @property 

1344 def raise_for_status( 

1345 self, 

1346 ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: 

1347 """Should `ClientResponse.raise_for_status()` be called for each response.""" 

1348 return self._raise_for_status 

1349 

1350 @property 

1351 def auto_decompress(self) -> bool: 

1352 """Should the body response be automatically decompressed.""" 

1353 return self._auto_decompress 

1354 

1355 @property 

1356 def trust_env(self) -> bool: 

1357 """ 

1358 Should proxies information from environment or netrc be trusted. 

1359 

1360 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1361 or ~/.netrc file if present. 

1362 """ 

1363 return self._trust_env 

1364 

1365 @property 

1366 def trace_configs(self) -> List[TraceConfig]: 

1367 """A list of TraceConfig instances used for client tracing""" 

1368 return self._trace_configs 

1369 

1370 def detach(self) -> None: 

1371 """Detach connector from session without closing the former. 

1372 

1373 Session is switched to closed state anyway. 

1374 """ 

1375 self._connector = None 

1376 

1377 def __enter__(self) -> None: 

1378 raise TypeError("Use async with instead") 

1379 

1380 def __exit__( 

1381 self, 

1382 exc_type: Optional[Type[BaseException]], 

1383 exc_val: Optional[BaseException], 

1384 exc_tb: Optional[TracebackType], 

1385 ) -> None: 

1386 # __exit__ should exist in pair with __enter__ but never executed 

1387 pass # pragma: no cover 

1388 

1389 async def __aenter__(self) -> "ClientSession": 

1390 return self 

1391 

1392 async def __aexit__( 

1393 self, 

1394 exc_type: Optional[Type[BaseException]], 

1395 exc_val: Optional[BaseException], 

1396 exc_tb: Optional[TracebackType], 

1397 ) -> None: 

1398 await self.close() 

1399 

1400 

1401class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]): 

1402 

1403 __slots__ = ("_coro", "_resp") 

1404 

1405 def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: 

1406 self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro 

1407 

1408 def send(self, arg: None) -> "asyncio.Future[Any]": 

1409 return self._coro.send(arg) 

1410 

1411 def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]": 

1412 return self._coro.throw(*args, **kwargs) 

1413 

1414 def close(self) -> None: 

1415 return self._coro.close() 

1416 

1417 def __await__(self) -> Generator[Any, None, _RetType]: 

1418 ret = self._coro.__await__() 

1419 return ret 

1420 

1421 def __iter__(self) -> Generator[Any, None, _RetType]: 

1422 return self.__await__() 

1423 

1424 async def __aenter__(self) -> _RetType: 

1425 self._resp: _RetType = await self._coro 

1426 return await self._resp.__aenter__() 

1427 

1428 async def __aexit__( 

1429 self, 

1430 exc_type: Optional[Type[BaseException]], 

1431 exc: Optional[BaseException], 

1432 tb: Optional[TracebackType], 

1433 ) -> None: 

1434 await self._resp.__aexit__(exc_type, exc, tb) 

1435 

1436 

1437_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1438_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] 

1439 

1440 

1441class _SessionRequestContextManager: 

1442 

1443 __slots__ = ("_coro", "_resp", "_session") 

1444 

1445 def __init__( 

1446 self, 

1447 coro: Coroutine["asyncio.Future[Any]", None, ClientResponse], 

1448 session: ClientSession, 

1449 ) -> None: 

1450 self._coro = coro 

1451 self._resp: Optional[ClientResponse] = None 

1452 self._session = session 

1453 

1454 async def __aenter__(self) -> ClientResponse: 

1455 try: 

1456 self._resp = await self._coro 

1457 except BaseException: 

1458 await self._session.close() 

1459 raise 

1460 else: 

1461 return self._resp 

1462 

1463 async def __aexit__( 

1464 self, 

1465 exc_type: Optional[Type[BaseException]], 

1466 exc: Optional[BaseException], 

1467 tb: Optional[TracebackType], 

1468 ) -> None: 

1469 assert self._resp is not None 

1470 self._resp.close() 

1471 await self._session.close() 

1472 

1473 

1474if sys.version_info >= (3, 11) and TYPE_CHECKING: 

1475 

1476 def request( 

1477 method: str, 

1478 url: StrOrURL, 

1479 *, 

1480 version: HttpVersion = http.HttpVersion11, 

1481 connector: Optional[BaseConnector] = None, 

1482 loop: Optional[asyncio.AbstractEventLoop] = None, 

1483 **kwargs: Unpack[_RequestOptions], 

1484 ) -> _SessionRequestContextManager: ... 

1485 

1486else: 

1487 

1488 def request( 

1489 method: str, 

1490 url: StrOrURL, 

1491 *, 

1492 version: HttpVersion = http.HttpVersion11, 

1493 connector: Optional[BaseConnector] = None, 

1494 loop: Optional[asyncio.AbstractEventLoop] = None, 

1495 **kwargs: Any, 

1496 ) -> _SessionRequestContextManager: 

1497 """Constructs and sends a request. 

1498 

1499 Returns response object. 

1500 method - HTTP method 

1501 url - request url 

1502 params - (optional) Dictionary or bytes to be sent in the query 

1503 string of the new request 

1504 data - (optional) Dictionary, bytes, or file-like object to 

1505 send in the body of the request 

1506 json - (optional) Any json compatible python object 

1507 headers - (optional) Dictionary of HTTP Headers to send with 

1508 the request 

1509 cookies - (optional) Dict object to send with the request 

1510 auth - (optional) BasicAuth named tuple represent HTTP Basic Auth 

1511 auth - aiohttp.helpers.BasicAuth 

1512 allow_redirects - (optional) If set to False, do not follow 

1513 redirects 

1514 version - Request HTTP version. 

1515 compress - Set to True if request has to be compressed 

1516 with deflate encoding. 

1517 chunked - Set to chunk size for chunked transfer encoding. 

1518 expect100 - Expect 100-continue response from server. 

1519 connector - BaseConnector sub-class instance to support 

1520 connection pooling. 

1521 read_until_eof - Read response until eof if response 

1522 does not have Content-Length header. 

1523 loop - Optional event loop. 

1524 timeout - Optional ClientTimeout settings structure, 5min 

1525 total timeout by default. 

1526 Usage:: 

1527 >>> import aiohttp 

1528 >>> async with aiohttp.request('GET', 'http://python.org/') as resp: 

1529 ... print(resp) 

1530 ... data = await resp.read() 

1531 <ClientResponse(https://www.python.org/) [200 OK]> 

1532 """ 

1533 connector_owner = False 

1534 if connector is None: 

1535 connector_owner = True 

1536 connector = TCPConnector(loop=loop, force_close=True) 

1537 

1538 session = ClientSession( 

1539 loop=loop, 

1540 cookies=kwargs.pop("cookies", None), 

1541 version=version, 

1542 timeout=kwargs.pop("timeout", sentinel), 

1543 connector=connector, 

1544 connector_owner=connector_owner, 

1545 ) 

1546 

1547 return _SessionRequestContextManager( 

1548 session._request(method, url, **kwargs), 

1549 session, 

1550 )