Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/web.py: 29%

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

133 statements  

1import asyncio 

2import logging 

3import os 

4import socket 

5import sys 

6import warnings 

7from argparse import ArgumentParser 

8from collections.abc import Iterable 

9from contextlib import suppress 

10from importlib import import_module 

11from typing import ( 

12 TYPE_CHECKING, 

13 Any, 

14 Awaitable, 

15 Callable, 

16 Iterable as TypingIterable, 

17 List, 

18 Optional, 

19 Set, 

20 Type, 

21 Union, 

22 cast, 

23) 

24 

25from .abc import AbstractAccessLogger 

26from .helpers import AppKey 

27from .log import access_logger 

28from .typedefs import PathLike 

29from .web_app import Application, CleanupError 

30from .web_exceptions import ( 

31 HTTPAccepted, 

32 HTTPBadGateway, 

33 HTTPBadRequest, 

34 HTTPClientError, 

35 HTTPConflict, 

36 HTTPCreated, 

37 HTTPError, 

38 HTTPException, 

39 HTTPExpectationFailed, 

40 HTTPFailedDependency, 

41 HTTPForbidden, 

42 HTTPFound, 

43 HTTPGatewayTimeout, 

44 HTTPGone, 

45 HTTPInsufficientStorage, 

46 HTTPInternalServerError, 

47 HTTPLengthRequired, 

48 HTTPMethodNotAllowed, 

49 HTTPMisdirectedRequest, 

50 HTTPMove, 

51 HTTPMovedPermanently, 

52 HTTPMultipleChoices, 

53 HTTPNetworkAuthenticationRequired, 

54 HTTPNoContent, 

55 HTTPNonAuthoritativeInformation, 

56 HTTPNotAcceptable, 

57 HTTPNotExtended, 

58 HTTPNotFound, 

59 HTTPNotImplemented, 

60 HTTPNotModified, 

61 HTTPOk, 

62 HTTPPartialContent, 

63 HTTPPaymentRequired, 

64 HTTPPermanentRedirect, 

65 HTTPPreconditionFailed, 

66 HTTPPreconditionRequired, 

67 HTTPProxyAuthenticationRequired, 

68 HTTPRedirection, 

69 HTTPRequestEntityTooLarge, 

70 HTTPRequestHeaderFieldsTooLarge, 

71 HTTPRequestRangeNotSatisfiable, 

72 HTTPRequestTimeout, 

73 HTTPRequestURITooLong, 

74 HTTPResetContent, 

75 HTTPSeeOther, 

76 HTTPServerError, 

77 HTTPServiceUnavailable, 

78 HTTPSuccessful, 

79 HTTPTemporaryRedirect, 

80 HTTPTooManyRequests, 

81 HTTPUnauthorized, 

82 HTTPUnavailableForLegalReasons, 

83 HTTPUnprocessableEntity, 

84 HTTPUnsupportedMediaType, 

85 HTTPUpgradeRequired, 

86 HTTPUseProxy, 

87 HTTPVariantAlsoNegotiates, 

88 HTTPVersionNotSupported, 

89 NotAppKeyWarning, 

90) 

91from .web_fileresponse import FileResponse 

92from .web_log import AccessLogger 

93from .web_middlewares import middleware, normalize_path_middleware 

94from .web_protocol import PayloadAccessError, RequestHandler, RequestPayloadError 

95from .web_request import BaseRequest, FileField, Request 

96from .web_response import ContentCoding, Response, StreamResponse, json_response 

97from .web_routedef import ( 

98 AbstractRouteDef, 

99 RouteDef, 

100 RouteTableDef, 

101 StaticDef, 

102 delete, 

103 get, 

104 head, 

105 options, 

106 patch, 

107 post, 

108 put, 

109 route, 

110 static, 

111 view, 

112) 

113from .web_runner import ( 

114 AppRunner, 

115 BaseRunner, 

116 BaseSite, 

117 GracefulExit, 

118 NamedPipeSite, 

119 ServerRunner, 

120 SockSite, 

121 TCPSite, 

122 UnixSite, 

123) 

124from .web_server import Server 

125from .web_urldispatcher import ( 

126 AbstractResource, 

127 AbstractRoute, 

128 DynamicResource, 

129 PlainResource, 

130 PrefixedSubAppResource, 

131 Resource, 

132 ResourceRoute, 

133 StaticResource, 

134 UrlDispatcher, 

135 UrlMappingMatchInfo, 

136 View, 

137) 

138from .web_ws import WebSocketReady, WebSocketResponse, WSMsgType 

139 

140__all__ = ( 

141 # web_app 

142 "AppKey", 

143 "Application", 

144 "CleanupError", 

145 # web_exceptions 

146 "NotAppKeyWarning", 

147 "HTTPAccepted", 

148 "HTTPBadGateway", 

149 "HTTPBadRequest", 

150 "HTTPClientError", 

151 "HTTPConflict", 

152 "HTTPCreated", 

153 "HTTPError", 

154 "HTTPException", 

155 "HTTPExpectationFailed", 

156 "HTTPFailedDependency", 

157 "HTTPForbidden", 

158 "HTTPFound", 

159 "HTTPGatewayTimeout", 

160 "HTTPGone", 

161 "HTTPInsufficientStorage", 

162 "HTTPInternalServerError", 

163 "HTTPLengthRequired", 

164 "HTTPMethodNotAllowed", 

165 "HTTPMisdirectedRequest", 

166 "HTTPMove", 

167 "HTTPMovedPermanently", 

168 "HTTPMultipleChoices", 

169 "HTTPNetworkAuthenticationRequired", 

170 "HTTPNoContent", 

171 "HTTPNonAuthoritativeInformation", 

172 "HTTPNotAcceptable", 

173 "HTTPNotExtended", 

174 "HTTPNotFound", 

175 "HTTPNotImplemented", 

176 "HTTPNotModified", 

177 "HTTPOk", 

178 "HTTPPartialContent", 

179 "HTTPPaymentRequired", 

180 "HTTPPermanentRedirect", 

181 "HTTPPreconditionFailed", 

182 "HTTPPreconditionRequired", 

183 "HTTPProxyAuthenticationRequired", 

184 "HTTPRedirection", 

185 "HTTPRequestEntityTooLarge", 

186 "HTTPRequestHeaderFieldsTooLarge", 

187 "HTTPRequestRangeNotSatisfiable", 

188 "HTTPRequestTimeout", 

189 "HTTPRequestURITooLong", 

190 "HTTPResetContent", 

191 "HTTPSeeOther", 

192 "HTTPServerError", 

193 "HTTPServiceUnavailable", 

194 "HTTPSuccessful", 

195 "HTTPTemporaryRedirect", 

196 "HTTPTooManyRequests", 

197 "HTTPUnauthorized", 

198 "HTTPUnavailableForLegalReasons", 

199 "HTTPUnprocessableEntity", 

200 "HTTPUnsupportedMediaType", 

201 "HTTPUpgradeRequired", 

202 "HTTPUseProxy", 

203 "HTTPVariantAlsoNegotiates", 

204 "HTTPVersionNotSupported", 

205 # web_fileresponse 

206 "FileResponse", 

207 # web_middlewares 

208 "middleware", 

209 "normalize_path_middleware", 

210 # web_protocol 

211 "PayloadAccessError", 

212 "RequestHandler", 

213 "RequestPayloadError", 

214 # web_request 

215 "BaseRequest", 

216 "FileField", 

217 "Request", 

218 # web_response 

219 "ContentCoding", 

220 "Response", 

221 "StreamResponse", 

222 "json_response", 

223 # web_routedef 

224 "AbstractRouteDef", 

225 "RouteDef", 

226 "RouteTableDef", 

227 "StaticDef", 

228 "delete", 

229 "get", 

230 "head", 

231 "options", 

232 "patch", 

233 "post", 

234 "put", 

235 "route", 

236 "static", 

237 "view", 

238 # web_runner 

239 "AppRunner", 

240 "BaseRunner", 

241 "BaseSite", 

242 "GracefulExit", 

243 "ServerRunner", 

244 "SockSite", 

245 "TCPSite", 

246 "UnixSite", 

247 "NamedPipeSite", 

248 # web_server 

249 "Server", 

250 # web_urldispatcher 

251 "AbstractResource", 

252 "AbstractRoute", 

253 "DynamicResource", 

254 "PlainResource", 

255 "PrefixedSubAppResource", 

256 "Resource", 

257 "ResourceRoute", 

258 "StaticResource", 

259 "UrlDispatcher", 

260 "UrlMappingMatchInfo", 

261 "View", 

262 # web_ws 

263 "WebSocketReady", 

264 "WebSocketResponse", 

265 "WSMsgType", 

266 # web 

267 "run_app", 

268) 

269 

270 

271if TYPE_CHECKING: 

272 from ssl import SSLContext 

273else: 

274 try: 

275 from ssl import SSLContext 

276 except ImportError: # pragma: no cover 

277 SSLContext = object # type: ignore[misc,assignment] 

278 

279# Only display warning when using -Wdefault, -We, -X dev or similar. 

280warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True) 

281 

282HostSequence = TypingIterable[str] 

283 

284 

285async def _run_app( 

286 app: Union[Application, Awaitable[Application]], 

287 *, 

288 host: Optional[Union[str, HostSequence]] = None, 

289 port: Optional[int] = None, 

290 path: Union[PathLike, TypingIterable[PathLike], None] = None, 

291 sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, 

292 shutdown_timeout: float = 60.0, 

293 keepalive_timeout: float = 75.0, 

294 ssl_context: Optional[SSLContext] = None, 

295 print: Optional[Callable[..., None]] = print, 

296 backlog: int = 128, 

297 access_log_class: Type[AbstractAccessLogger] = AccessLogger, 

298 access_log_format: str = AccessLogger.LOG_FORMAT, 

299 access_log: Optional[logging.Logger] = access_logger, 

300 handle_signals: bool = True, 

301 reuse_address: Optional[bool] = None, 

302 reuse_port: Optional[bool] = None, 

303 handler_cancellation: bool = False, 

304) -> None: 

305 # An internal function to actually do all dirty job for application running 

306 if asyncio.iscoroutine(app): 

307 app = await app 

308 

309 app = cast(Application, app) 

310 

311 runner = AppRunner( 

312 app, 

313 handle_signals=handle_signals, 

314 access_log_class=access_log_class, 

315 access_log_format=access_log_format, 

316 access_log=access_log, 

317 keepalive_timeout=keepalive_timeout, 

318 shutdown_timeout=shutdown_timeout, 

319 handler_cancellation=handler_cancellation, 

320 ) 

321 

322 await runner.setup() 

323 

324 sites: List[BaseSite] = [] 

325 

326 try: 

327 if host is not None: 

328 if isinstance(host, str): 

329 sites.append( 

330 TCPSite( 

331 runner, 

332 host, 

333 port, 

334 ssl_context=ssl_context, 

335 backlog=backlog, 

336 reuse_address=reuse_address, 

337 reuse_port=reuse_port, 

338 ) 

339 ) 

340 else: 

341 for h in host: 

342 sites.append( 

343 TCPSite( 

344 runner, 

345 h, 

346 port, 

347 ssl_context=ssl_context, 

348 backlog=backlog, 

349 reuse_address=reuse_address, 

350 reuse_port=reuse_port, 

351 ) 

352 ) 

353 elif path is None and sock is None or port is not None: 

354 sites.append( 

355 TCPSite( 

356 runner, 

357 port=port, 

358 ssl_context=ssl_context, 

359 backlog=backlog, 

360 reuse_address=reuse_address, 

361 reuse_port=reuse_port, 

362 ) 

363 ) 

364 

365 if path is not None: 

366 if isinstance(path, (str, os.PathLike)): 

367 sites.append( 

368 UnixSite( 

369 runner, 

370 path, 

371 ssl_context=ssl_context, 

372 backlog=backlog, 

373 ) 

374 ) 

375 else: 

376 for p in path: 

377 sites.append( 

378 UnixSite( 

379 runner, 

380 p, 

381 ssl_context=ssl_context, 

382 backlog=backlog, 

383 ) 

384 ) 

385 

386 if sock is not None: 

387 if not isinstance(sock, Iterable): 

388 sites.append( 

389 SockSite( 

390 runner, 

391 sock, 

392 ssl_context=ssl_context, 

393 backlog=backlog, 

394 ) 

395 ) 

396 else: 

397 for s in sock: 

398 sites.append( 

399 SockSite( 

400 runner, 

401 s, 

402 ssl_context=ssl_context, 

403 backlog=backlog, 

404 ) 

405 ) 

406 for site in sites: 

407 await site.start() 

408 

409 if print: # pragma: no branch 

410 names = sorted(str(s.name) for s in runner.sites) 

411 print( 

412 "======== Running on {} ========\n" 

413 "(Press CTRL+C to quit)".format(", ".join(names)) 

414 ) 

415 

416 # sleep forever by 1 hour intervals, 

417 while True: 

418 await asyncio.sleep(3600) 

419 finally: 

420 await runner.cleanup() 

421 

422 

423def _cancel_tasks( 

424 to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop 

425) -> None: 

426 if not to_cancel: 

427 return 

428 

429 for task in to_cancel: 

430 task.cancel() 

431 

432 loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) 

433 

434 for task in to_cancel: 

435 if task.cancelled(): 

436 continue 

437 if task.exception() is not None: 

438 loop.call_exception_handler( 

439 { 

440 "message": "unhandled exception during asyncio.run() shutdown", 

441 "exception": task.exception(), 

442 "task": task, 

443 } 

444 ) 

445 

446 

447def run_app( 

448 app: Union[Application, Awaitable[Application]], 

449 *, 

450 debug: bool = False, 

451 host: Optional[Union[str, HostSequence]] = None, 

452 port: Optional[int] = None, 

453 path: Union[PathLike, TypingIterable[PathLike], None] = None, 

454 sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, 

455 shutdown_timeout: float = 60.0, 

456 keepalive_timeout: float = 75.0, 

457 ssl_context: Optional[SSLContext] = None, 

458 print: Optional[Callable[..., None]] = print, 

459 backlog: int = 128, 

460 access_log_class: Type[AbstractAccessLogger] = AccessLogger, 

461 access_log_format: str = AccessLogger.LOG_FORMAT, 

462 access_log: Optional[logging.Logger] = access_logger, 

463 handle_signals: bool = True, 

464 reuse_address: Optional[bool] = None, 

465 reuse_port: Optional[bool] = None, 

466 handler_cancellation: bool = False, 

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

468) -> None: 

469 """Run an app locally""" 

470 if loop is None: 

471 loop = asyncio.new_event_loop() 

472 loop.set_debug(debug) 

473 

474 # Configure if and only if in debugging mode and using the default logger 

475 if loop.get_debug() and access_log and access_log.name == "aiohttp.access": 

476 if access_log.level == logging.NOTSET: 

477 access_log.setLevel(logging.DEBUG) 

478 if not access_log.hasHandlers(): 

479 access_log.addHandler(logging.StreamHandler()) 

480 

481 main_task = loop.create_task( 

482 _run_app( 

483 app, 

484 host=host, 

485 port=port, 

486 path=path, 

487 sock=sock, 

488 shutdown_timeout=shutdown_timeout, 

489 keepalive_timeout=keepalive_timeout, 

490 ssl_context=ssl_context, 

491 print=print, 

492 backlog=backlog, 

493 access_log_class=access_log_class, 

494 access_log_format=access_log_format, 

495 access_log=access_log, 

496 handle_signals=handle_signals, 

497 reuse_address=reuse_address, 

498 reuse_port=reuse_port, 

499 handler_cancellation=handler_cancellation, 

500 ) 

501 ) 

502 

503 try: 

504 asyncio.set_event_loop(loop) 

505 loop.run_until_complete(main_task) 

506 except (GracefulExit, KeyboardInterrupt): 

507 pass 

508 finally: 

509 try: 

510 main_task.cancel() 

511 with suppress(asyncio.CancelledError): 

512 loop.run_until_complete(main_task) 

513 finally: 

514 _cancel_tasks(asyncio.all_tasks(loop), loop) 

515 loop.run_until_complete(loop.shutdown_asyncgens()) 

516 loop.close() 

517 asyncio.set_event_loop(None) 

518 

519 

520def main(argv: List[str]) -> None: 

521 arg_parser = ArgumentParser( 

522 description="aiohttp.web Application server", prog="aiohttp.web" 

523 ) 

524 arg_parser.add_argument( 

525 "entry_func", 

526 help=( 

527 "Callable returning the `aiohttp.web.Application` instance to " 

528 "run. Should be specified in the 'module:function' syntax." 

529 ), 

530 metavar="entry-func", 

531 ) 

532 arg_parser.add_argument( 

533 "-H", 

534 "--hostname", 

535 help="TCP/IP hostname to serve on (default: localhost)", 

536 default=None, 

537 ) 

538 arg_parser.add_argument( 

539 "-P", 

540 "--port", 

541 help="TCP/IP port to serve on (default: %(default)r)", 

542 type=int, 

543 default=8080, 

544 ) 

545 arg_parser.add_argument( 

546 "-U", 

547 "--path", 

548 help="Unix file system path to serve on. Can be combined with hostname " 

549 "to serve on both Unix and TCP.", 

550 ) 

551 args, extra_argv = arg_parser.parse_known_args(argv) 

552 

553 # Import logic 

554 mod_str, _, func_str = args.entry_func.partition(":") 

555 if not func_str or not mod_str: 

556 arg_parser.error("'entry-func' not in 'module:function' syntax") 

557 if mod_str.startswith("."): 

558 arg_parser.error("relative module names not supported") 

559 try: 

560 module = import_module(mod_str) 

561 except ImportError as ex: 

562 arg_parser.error(f"unable to import {mod_str}: {ex}") 

563 try: 

564 func = getattr(module, func_str) 

565 except AttributeError: 

566 arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}") 

567 

568 # Compatibility logic 

569 if args.path is not None and not hasattr(socket, "AF_UNIX"): 

570 arg_parser.error( 

571 "file system paths not supported by your operating environment" 

572 ) 

573 

574 logging.basicConfig(level=logging.DEBUG) 

575 

576 if args.path and args.hostname is None: 

577 host = port = None 

578 else: 

579 host = args.hostname or "localhost" 

580 port = args.port 

581 

582 app = func(extra_argv) 

583 run_app(app, host=host, port=port, path=args.path) 

584 arg_parser.exit(message="Stopped\n") 

585 

586 

587if __name__ == "__main__": # pragma: no branch 

588 main(sys.argv[1:]) # pragma: no cover