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

121 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:52 +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 # on Windows before Python 3.8 wake up every 1 second to handle 

413 # Ctrl+C smoothly 

414 if sys.platform == "win32" and sys.version_info < (3, 8): 

415 delay = 1 

416 else: 

417 delay = 3600 

418 

419 while True: 

420 await asyncio.sleep(delay) 

421 finally: 

422 await runner.cleanup() 

423 

424 

425def _cancel_tasks( 

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

427) -> None: 

428 if not to_cancel: 

429 return 

430 

431 for task in to_cancel: 

432 task.cancel() 

433 

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

435 

436 for task in to_cancel: 

437 if task.cancelled(): 

438 continue 

439 if task.exception() is not None: 

440 loop.call_exception_handler( 

441 { 

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

443 "exception": task.exception(), 

444 "task": task, 

445 } 

446 ) 

447 

448 

449def run_app( 

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

451 *, 

452 debug: bool = False, 

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

454 port: Optional[int] = None, 

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

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

457 shutdown_timeout: float = 60.0, 

458 keepalive_timeout: float = 75.0, 

459 ssl_context: Optional[SSLContext] = None, 

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

461 backlog: int = 128, 

462 access_log_class: Type[AbstractAccessLogger] = AccessLogger, 

463 access_log_format: str = AccessLogger.LOG_FORMAT, 

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

465 handle_signals: bool = True, 

466 reuse_address: Optional[bool] = None, 

467 reuse_port: Optional[bool] = None, 

468 handler_cancellation: bool = False, 

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

470) -> None: 

471 """Run an app locally""" 

472 if loop is None: 

473 loop = asyncio.new_event_loop() 

474 loop.set_debug(debug) 

475 

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

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

478 if access_log.level == logging.NOTSET: 

479 access_log.setLevel(logging.DEBUG) 

480 if not access_log.hasHandlers(): 

481 access_log.addHandler(logging.StreamHandler()) 

482 

483 main_task = loop.create_task( 

484 _run_app( 

485 app, 

486 host=host, 

487 port=port, 

488 path=path, 

489 sock=sock, 

490 shutdown_timeout=shutdown_timeout, 

491 keepalive_timeout=keepalive_timeout, 

492 ssl_context=ssl_context, 

493 print=print, 

494 backlog=backlog, 

495 access_log_class=access_log_class, 

496 access_log_format=access_log_format, 

497 access_log=access_log, 

498 handle_signals=handle_signals, 

499 reuse_address=reuse_address, 

500 reuse_port=reuse_port, 

501 handler_cancellation=handler_cancellation, 

502 ) 

503 ) 

504 

505 try: 

506 asyncio.set_event_loop(loop) 

507 loop.run_until_complete(main_task) 

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

509 pass 

510 finally: 

511 _cancel_tasks({main_task}, loop) 

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

513 loop.run_until_complete(loop.shutdown_asyncgens()) 

514 loop.close() 

515 asyncio.set_event_loop(None) 

516 

517 

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

519 arg_parser = ArgumentParser( 

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

521 ) 

522 arg_parser.add_argument( 

523 "entry_func", 

524 help=( 

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

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

527 ), 

528 metavar="entry-func", 

529 ) 

530 arg_parser.add_argument( 

531 "-H", 

532 "--hostname", 

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

534 default="localhost", 

535 ) 

536 arg_parser.add_argument( 

537 "-P", 

538 "--port", 

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

540 type=int, 

541 default="8080", 

542 ) 

543 arg_parser.add_argument( 

544 "-U", 

545 "--path", 

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

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

548 ) 

549 args, extra_argv = arg_parser.parse_known_args(argv) 

550 

551 # Import logic 

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

553 if not func_str or not mod_str: 

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

555 if mod_str.startswith("."): 

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

557 try: 

558 module = import_module(mod_str) 

559 except ImportError as ex: 

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

561 try: 

562 func = getattr(module, func_str) 

563 except AttributeError: 

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

565 

566 # Compatibility logic 

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

568 arg_parser.error( 

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

570 ) 

571 

572 logging.basicConfig(level=logging.DEBUG) 

573 

574 app = func(extra_argv) 

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

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

577 

578 

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

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