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

118 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-27 06:09 +0000

1import asyncio 

2import logging 

3import os 

4import socket 

5import sys 

6from argparse import ArgumentParser 

7from collections.abc import Iterable 

8from importlib import import_module 

9from typing import ( 

10 Any, 

11 Awaitable, 

12 Callable, 

13 Iterable as TypingIterable, 

14 List, 

15 Optional, 

16 Set, 

17 Type, 

18 Union, 

19 cast, 

20) 

21 

22from .abc import AbstractAccessLogger 

23from .helpers import AppKey 

24from .log import access_logger 

25from .typedefs import PathLike 

26from .web_app import Application, CleanupError 

27from .web_exceptions import ( 

28 HTTPAccepted, 

29 HTTPBadGateway, 

30 HTTPBadRequest, 

31 HTTPClientError, 

32 HTTPConflict, 

33 HTTPCreated, 

34 HTTPError, 

35 HTTPException, 

36 HTTPExpectationFailed, 

37 HTTPFailedDependency, 

38 HTTPForbidden, 

39 HTTPFound, 

40 HTTPGatewayTimeout, 

41 HTTPGone, 

42 HTTPInsufficientStorage, 

43 HTTPInternalServerError, 

44 HTTPLengthRequired, 

45 HTTPMethodNotAllowed, 

46 HTTPMisdirectedRequest, 

47 HTTPMove, 

48 HTTPMovedPermanently, 

49 HTTPMultipleChoices, 

50 HTTPNetworkAuthenticationRequired, 

51 HTTPNoContent, 

52 HTTPNonAuthoritativeInformation, 

53 HTTPNotAcceptable, 

54 HTTPNotExtended, 

55 HTTPNotFound, 

56 HTTPNotImplemented, 

57 HTTPNotModified, 

58 HTTPOk, 

59 HTTPPartialContent, 

60 HTTPPaymentRequired, 

61 HTTPPermanentRedirect, 

62 HTTPPreconditionFailed, 

63 HTTPPreconditionRequired, 

64 HTTPProxyAuthenticationRequired, 

65 HTTPRedirection, 

66 HTTPRequestEntityTooLarge, 

67 HTTPRequestHeaderFieldsTooLarge, 

68 HTTPRequestRangeNotSatisfiable, 

69 HTTPRequestTimeout, 

70 HTTPRequestURITooLong, 

71 HTTPResetContent, 

72 HTTPSeeOther, 

73 HTTPServerError, 

74 HTTPServiceUnavailable, 

75 HTTPSuccessful, 

76 HTTPTemporaryRedirect, 

77 HTTPTooManyRequests, 

78 HTTPUnauthorized, 

79 HTTPUnavailableForLegalReasons, 

80 HTTPUnprocessableEntity, 

81 HTTPUnsupportedMediaType, 

82 HTTPUpgradeRequired, 

83 HTTPUseProxy, 

84 HTTPVariantAlsoNegotiates, 

85 HTTPVersionNotSupported, 

86) 

87from .web_fileresponse import FileResponse 

88from .web_log import AccessLogger 

89from .web_middlewares import middleware, normalize_path_middleware 

90from .web_protocol import PayloadAccessError, RequestHandler, RequestPayloadError 

91from .web_request import BaseRequest, FileField, Request 

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

93from .web_routedef import ( 

94 AbstractRouteDef, 

95 RouteDef, 

96 RouteTableDef, 

97 StaticDef, 

98 delete, 

99 get, 

100 head, 

101 options, 

102 patch, 

103 post, 

104 put, 

105 route, 

106 static, 

107 view, 

108) 

109from .web_runner import ( 

110 AppRunner, 

111 BaseRunner, 

112 BaseSite, 

113 GracefulExit, 

114 NamedPipeSite, 

115 ServerRunner, 

116 SockSite, 

117 TCPSite, 

118 UnixSite, 

119) 

120from .web_server import Server 

121from .web_urldispatcher import ( 

122 AbstractResource, 

123 AbstractRoute, 

124 DynamicResource, 

125 PlainResource, 

126 PrefixedSubAppResource, 

127 Resource, 

128 ResourceRoute, 

129 StaticResource, 

130 UrlDispatcher, 

131 UrlMappingMatchInfo, 

132 View, 

133) 

134from .web_ws import WebSocketReady, WebSocketResponse, WSMsgType 

135 

136__all__ = ( 

137 # web_app 

138 "AppKey", 

139 "Application", 

140 "CleanupError", 

141 # web_exceptions 

142 "HTTPAccepted", 

143 "HTTPBadGateway", 

144 "HTTPBadRequest", 

145 "HTTPClientError", 

146 "HTTPConflict", 

147 "HTTPCreated", 

148 "HTTPError", 

149 "HTTPException", 

150 "HTTPExpectationFailed", 

151 "HTTPFailedDependency", 

152 "HTTPForbidden", 

153 "HTTPFound", 

154 "HTTPGatewayTimeout", 

155 "HTTPGone", 

156 "HTTPInsufficientStorage", 

157 "HTTPInternalServerError", 

158 "HTTPLengthRequired", 

159 "HTTPMethodNotAllowed", 

160 "HTTPMisdirectedRequest", 

161 "HTTPMove", 

162 "HTTPMovedPermanently", 

163 "HTTPMultipleChoices", 

164 "HTTPNetworkAuthenticationRequired", 

165 "HTTPNoContent", 

166 "HTTPNonAuthoritativeInformation", 

167 "HTTPNotAcceptable", 

168 "HTTPNotExtended", 

169 "HTTPNotFound", 

170 "HTTPNotImplemented", 

171 "HTTPNotModified", 

172 "HTTPOk", 

173 "HTTPPartialContent", 

174 "HTTPPaymentRequired", 

175 "HTTPPermanentRedirect", 

176 "HTTPPreconditionFailed", 

177 "HTTPPreconditionRequired", 

178 "HTTPProxyAuthenticationRequired", 

179 "HTTPRedirection", 

180 "HTTPRequestEntityTooLarge", 

181 "HTTPRequestHeaderFieldsTooLarge", 

182 "HTTPRequestRangeNotSatisfiable", 

183 "HTTPRequestTimeout", 

184 "HTTPRequestURITooLong", 

185 "HTTPResetContent", 

186 "HTTPSeeOther", 

187 "HTTPServerError", 

188 "HTTPServiceUnavailable", 

189 "HTTPSuccessful", 

190 "HTTPTemporaryRedirect", 

191 "HTTPTooManyRequests", 

192 "HTTPUnauthorized", 

193 "HTTPUnavailableForLegalReasons", 

194 "HTTPUnprocessableEntity", 

195 "HTTPUnsupportedMediaType", 

196 "HTTPUpgradeRequired", 

197 "HTTPUseProxy", 

198 "HTTPVariantAlsoNegotiates", 

199 "HTTPVersionNotSupported", 

200 # web_fileresponse 

201 "FileResponse", 

202 # web_middlewares 

203 "middleware", 

204 "normalize_path_middleware", 

205 # web_protocol 

206 "PayloadAccessError", 

207 "RequestHandler", 

208 "RequestPayloadError", 

209 # web_request 

210 "BaseRequest", 

211 "FileField", 

212 "Request", 

213 # web_response 

214 "ContentCoding", 

215 "Response", 

216 "StreamResponse", 

217 "json_response", 

218 # web_routedef 

219 "AbstractRouteDef", 

220 "RouteDef", 

221 "RouteTableDef", 

222 "StaticDef", 

223 "delete", 

224 "get", 

225 "head", 

226 "options", 

227 "patch", 

228 "post", 

229 "put", 

230 "route", 

231 "static", 

232 "view", 

233 # web_runner 

234 "AppRunner", 

235 "BaseRunner", 

236 "BaseSite", 

237 "GracefulExit", 

238 "ServerRunner", 

239 "SockSite", 

240 "TCPSite", 

241 "UnixSite", 

242 "NamedPipeSite", 

243 # web_server 

244 "Server", 

245 # web_urldispatcher 

246 "AbstractResource", 

247 "AbstractRoute", 

248 "DynamicResource", 

249 "PlainResource", 

250 "PrefixedSubAppResource", 

251 "Resource", 

252 "ResourceRoute", 

253 "StaticResource", 

254 "UrlDispatcher", 

255 "UrlMappingMatchInfo", 

256 "View", 

257 # web_ws 

258 "WebSocketReady", 

259 "WebSocketResponse", 

260 "WSMsgType", 

261 # web 

262 "run_app", 

263) 

264 

265 

266try: 

267 from ssl import SSLContext 

268except ImportError: # pragma: no cover 

269 SSLContext = Any # type: ignore[misc,assignment] 

270 

271HostSequence = TypingIterable[str] 

272 

273 

274async def _run_app( 

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

276 *, 

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

278 port: Optional[int] = None, 

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

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

281 shutdown_timeout: float = 60.0, 

282 keepalive_timeout: float = 75.0, 

283 ssl_context: Optional[SSLContext] = None, 

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

285 backlog: int = 128, 

286 access_log_class: Type[AbstractAccessLogger] = AccessLogger, 

287 access_log_format: str = AccessLogger.LOG_FORMAT, 

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

289 handle_signals: bool = True, 

290 reuse_address: Optional[bool] = None, 

291 reuse_port: Optional[bool] = None, 

292 handler_cancellation: bool = False, 

293) -> None: 

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

295 if asyncio.iscoroutine(app): 

296 app = await app 

297 

298 app = cast(Application, app) 

299 

300 runner = AppRunner( 

301 app, 

302 handle_signals=handle_signals, 

303 access_log_class=access_log_class, 

304 access_log_format=access_log_format, 

305 access_log=access_log, 

306 keepalive_timeout=keepalive_timeout, 

307 handler_cancellation=handler_cancellation, 

308 ) 

309 

310 await runner.setup() 

311 

312 sites: List[BaseSite] = [] 

313 

314 try: 

315 if host is not None: 

316 if isinstance(host, (str, bytes, bytearray, memoryview)): 

317 sites.append( 

318 TCPSite( 

319 runner, 

320 host, 

321 port, 

322 shutdown_timeout=shutdown_timeout, 

323 ssl_context=ssl_context, 

324 backlog=backlog, 

325 reuse_address=reuse_address, 

326 reuse_port=reuse_port, 

327 ) 

328 ) 

329 else: 

330 for h in host: 

331 sites.append( 

332 TCPSite( 

333 runner, 

334 h, 

335 port, 

336 shutdown_timeout=shutdown_timeout, 

337 ssl_context=ssl_context, 

338 backlog=backlog, 

339 reuse_address=reuse_address, 

340 reuse_port=reuse_port, 

341 ) 

342 ) 

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

344 sites.append( 

345 TCPSite( 

346 runner, 

347 port=port, 

348 shutdown_timeout=shutdown_timeout, 

349 ssl_context=ssl_context, 

350 backlog=backlog, 

351 reuse_address=reuse_address, 

352 reuse_port=reuse_port, 

353 ) 

354 ) 

355 

356 if path is not None: 

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

358 sites.append( 

359 UnixSite( 

360 runner, 

361 path, 

362 shutdown_timeout=shutdown_timeout, 

363 ssl_context=ssl_context, 

364 backlog=backlog, 

365 ) 

366 ) 

367 else: 

368 for p in path: 

369 sites.append( 

370 UnixSite( 

371 runner, 

372 p, 

373 shutdown_timeout=shutdown_timeout, 

374 ssl_context=ssl_context, 

375 backlog=backlog, 

376 ) 

377 ) 

378 

379 if sock is not None: 

380 if not isinstance(sock, Iterable): 

381 sites.append( 

382 SockSite( 

383 runner, 

384 sock, 

385 shutdown_timeout=shutdown_timeout, 

386 ssl_context=ssl_context, 

387 backlog=backlog, 

388 ) 

389 ) 

390 else: 

391 for s in sock: 

392 sites.append( 

393 SockSite( 

394 runner, 

395 s, 

396 shutdown_timeout=shutdown_timeout, 

397 ssl_context=ssl_context, 

398 backlog=backlog, 

399 ) 

400 ) 

401 for site in sites: 

402 await site.start() 

403 

404 if print: # pragma: no branch 

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

406 print( 

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

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

409 ) 

410 

411 # sleep forever by 1 hour intervals, 

412 while True: 

413 await asyncio.sleep(3600) 

414 finally: 

415 await runner.cleanup() 

416 

417 

418def _cancel_tasks( 

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

420) -> None: 

421 if not to_cancel: 

422 return 

423 

424 for task in to_cancel: 

425 task.cancel() 

426 

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

428 

429 for task in to_cancel: 

430 if task.cancelled(): 

431 continue 

432 if task.exception() is not None: 

433 loop.call_exception_handler( 

434 { 

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

436 "exception": task.exception(), 

437 "task": task, 

438 } 

439 ) 

440 

441 

442def run_app( 

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

444 *, 

445 debug: bool = False, 

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

447 port: Optional[int] = None, 

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

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

450 shutdown_timeout: float = 60.0, 

451 keepalive_timeout: float = 75.0, 

452 ssl_context: Optional[SSLContext] = None, 

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

454 backlog: int = 128, 

455 access_log_class: Type[AbstractAccessLogger] = AccessLogger, 

456 access_log_format: str = AccessLogger.LOG_FORMAT, 

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

458 handle_signals: bool = True, 

459 reuse_address: Optional[bool] = None, 

460 reuse_port: Optional[bool] = None, 

461 handler_cancellation: bool = False, 

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

463) -> None: 

464 """Run an app locally""" 

465 if loop is None: 

466 loop = asyncio.new_event_loop() 

467 loop.set_debug(debug) 

468 

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

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

471 if access_log.level == logging.NOTSET: 

472 access_log.setLevel(logging.DEBUG) 

473 if not access_log.hasHandlers(): 

474 access_log.addHandler(logging.StreamHandler()) 

475 

476 main_task = loop.create_task( 

477 _run_app( 

478 app, 

479 host=host, 

480 port=port, 

481 path=path, 

482 sock=sock, 

483 shutdown_timeout=shutdown_timeout, 

484 keepalive_timeout=keepalive_timeout, 

485 ssl_context=ssl_context, 

486 print=print, 

487 backlog=backlog, 

488 access_log_class=access_log_class, 

489 access_log_format=access_log_format, 

490 access_log=access_log, 

491 handle_signals=handle_signals, 

492 reuse_address=reuse_address, 

493 reuse_port=reuse_port, 

494 handler_cancellation=handler_cancellation, 

495 ) 

496 ) 

497 

498 try: 

499 asyncio.set_event_loop(loop) 

500 loop.run_until_complete(main_task) 

501 except (GracefulExit, KeyboardInterrupt): # pragma: no cover 

502 pass 

503 finally: 

504 _cancel_tasks({main_task}, loop) 

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

506 loop.run_until_complete(loop.shutdown_asyncgens()) 

507 loop.close() 

508 asyncio.set_event_loop(None) 

509 

510 

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

512 arg_parser = ArgumentParser( 

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

514 ) 

515 arg_parser.add_argument( 

516 "entry_func", 

517 help=( 

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

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

520 ), 

521 metavar="entry-func", 

522 ) 

523 arg_parser.add_argument( 

524 "-H", 

525 "--hostname", 

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

527 default="localhost", 

528 ) 

529 arg_parser.add_argument( 

530 "-P", 

531 "--port", 

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

533 type=int, 

534 default="8080", 

535 ) 

536 arg_parser.add_argument( 

537 "-U", 

538 "--path", 

539 help="Unix file system path to serve on. Specifying a path will cause " 

540 "hostname and port arguments to be ignored.", 

541 ) 

542 args, extra_argv = arg_parser.parse_known_args(argv) 

543 

544 # Import logic 

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

546 if not func_str or not mod_str: 

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

548 if mod_str.startswith("."): 

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

550 try: 

551 module = import_module(mod_str) 

552 except ImportError as ex: 

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

554 try: 

555 func = getattr(module, func_str) 

556 except AttributeError: 

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

558 

559 # Compatibility logic 

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

561 arg_parser.error( 

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

563 ) 

564 

565 logging.basicConfig(level=logging.DEBUG) 

566 

567 app = func(extra_argv) 

568 run_app(app, host=args.hostname, port=args.port, path=args.path) 

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

570 

571 

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

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