Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/web.py: 29%
137 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-26 06:16 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-26 06:16 +0000
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 functools import partial
11from importlib import import_module
12from typing import (
13 Any,
14 Awaitable,
15 Callable,
16 Iterable as TypingIterable,
17 List,
18 Optional,
19 Set,
20 Type,
21 Union,
22 cast,
23)
24from weakref import WeakSet
26from .abc import AbstractAccessLogger
27from .helpers import AppKey
28from .log import access_logger
29from .typedefs import PathLike
30from .web_app import Application, CleanupError
31from .web_exceptions import (
32 HTTPAccepted,
33 HTTPBadGateway,
34 HTTPBadRequest,
35 HTTPClientError,
36 HTTPConflict,
37 HTTPCreated,
38 HTTPError,
39 HTTPException,
40 HTTPExpectationFailed,
41 HTTPFailedDependency,
42 HTTPForbidden,
43 HTTPFound,
44 HTTPGatewayTimeout,
45 HTTPGone,
46 HTTPInsufficientStorage,
47 HTTPInternalServerError,
48 HTTPLengthRequired,
49 HTTPMethodNotAllowed,
50 HTTPMisdirectedRequest,
51 HTTPMove,
52 HTTPMovedPermanently,
53 HTTPMultipleChoices,
54 HTTPNetworkAuthenticationRequired,
55 HTTPNoContent,
56 HTTPNonAuthoritativeInformation,
57 HTTPNotAcceptable,
58 HTTPNotExtended,
59 HTTPNotFound,
60 HTTPNotImplemented,
61 HTTPNotModified,
62 HTTPOk,
63 HTTPPartialContent,
64 HTTPPaymentRequired,
65 HTTPPermanentRedirect,
66 HTTPPreconditionFailed,
67 HTTPPreconditionRequired,
68 HTTPProxyAuthenticationRequired,
69 HTTPRedirection,
70 HTTPRequestEntityTooLarge,
71 HTTPRequestHeaderFieldsTooLarge,
72 HTTPRequestRangeNotSatisfiable,
73 HTTPRequestTimeout,
74 HTTPRequestURITooLong,
75 HTTPResetContent,
76 HTTPSeeOther,
77 HTTPServerError,
78 HTTPServiceUnavailable,
79 HTTPSuccessful,
80 HTTPTemporaryRedirect,
81 HTTPTooManyRequests,
82 HTTPUnauthorized,
83 HTTPUnavailableForLegalReasons,
84 HTTPUnprocessableEntity,
85 HTTPUnsupportedMediaType,
86 HTTPUpgradeRequired,
87 HTTPUseProxy,
88 HTTPVariantAlsoNegotiates,
89 HTTPVersionNotSupported,
90 NotAppKeyWarning,
91)
92from .web_fileresponse import FileResponse
93from .web_log import AccessLogger
94from .web_middlewares import middleware, normalize_path_middleware
95from .web_protocol import PayloadAccessError, RequestHandler, RequestPayloadError
96from .web_request import BaseRequest, FileField, Request
97from .web_response import ContentCoding, Response, StreamResponse, json_response
98from .web_routedef import (
99 AbstractRouteDef,
100 RouteDef,
101 RouteTableDef,
102 StaticDef,
103 delete,
104 get,
105 head,
106 options,
107 patch,
108 post,
109 put,
110 route,
111 static,
112 view,
113)
114from .web_runner import (
115 AppRunner,
116 BaseRunner,
117 BaseSite,
118 GracefulExit,
119 NamedPipeSite,
120 ServerRunner,
121 SockSite,
122 TCPSite,
123 UnixSite,
124)
125from .web_server import Server
126from .web_urldispatcher import (
127 AbstractResource,
128 AbstractRoute,
129 DynamicResource,
130 PlainResource,
131 PrefixedSubAppResource,
132 Resource,
133 ResourceRoute,
134 StaticResource,
135 UrlDispatcher,
136 UrlMappingMatchInfo,
137 View,
138)
139from .web_ws import WebSocketReady, WebSocketResponse, WSMsgType
141__all__ = (
142 # web_app
143 "AppKey",
144 "Application",
145 "CleanupError",
146 # web_exceptions
147 "NotAppKeyWarning",
148 "HTTPAccepted",
149 "HTTPBadGateway",
150 "HTTPBadRequest",
151 "HTTPClientError",
152 "HTTPConflict",
153 "HTTPCreated",
154 "HTTPError",
155 "HTTPException",
156 "HTTPExpectationFailed",
157 "HTTPFailedDependency",
158 "HTTPForbidden",
159 "HTTPFound",
160 "HTTPGatewayTimeout",
161 "HTTPGone",
162 "HTTPInsufficientStorage",
163 "HTTPInternalServerError",
164 "HTTPLengthRequired",
165 "HTTPMethodNotAllowed",
166 "HTTPMisdirectedRequest",
167 "HTTPMove",
168 "HTTPMovedPermanently",
169 "HTTPMultipleChoices",
170 "HTTPNetworkAuthenticationRequired",
171 "HTTPNoContent",
172 "HTTPNonAuthoritativeInformation",
173 "HTTPNotAcceptable",
174 "HTTPNotExtended",
175 "HTTPNotFound",
176 "HTTPNotImplemented",
177 "HTTPNotModified",
178 "HTTPOk",
179 "HTTPPartialContent",
180 "HTTPPaymentRequired",
181 "HTTPPermanentRedirect",
182 "HTTPPreconditionFailed",
183 "HTTPPreconditionRequired",
184 "HTTPProxyAuthenticationRequired",
185 "HTTPRedirection",
186 "HTTPRequestEntityTooLarge",
187 "HTTPRequestHeaderFieldsTooLarge",
188 "HTTPRequestRangeNotSatisfiable",
189 "HTTPRequestTimeout",
190 "HTTPRequestURITooLong",
191 "HTTPResetContent",
192 "HTTPSeeOther",
193 "HTTPServerError",
194 "HTTPServiceUnavailable",
195 "HTTPSuccessful",
196 "HTTPTemporaryRedirect",
197 "HTTPTooManyRequests",
198 "HTTPUnauthorized",
199 "HTTPUnavailableForLegalReasons",
200 "HTTPUnprocessableEntity",
201 "HTTPUnsupportedMediaType",
202 "HTTPUpgradeRequired",
203 "HTTPUseProxy",
204 "HTTPVariantAlsoNegotiates",
205 "HTTPVersionNotSupported",
206 # web_fileresponse
207 "FileResponse",
208 # web_middlewares
209 "middleware",
210 "normalize_path_middleware",
211 # web_protocol
212 "PayloadAccessError",
213 "RequestHandler",
214 "RequestPayloadError",
215 # web_request
216 "BaseRequest",
217 "FileField",
218 "Request",
219 # web_response
220 "ContentCoding",
221 "Response",
222 "StreamResponse",
223 "json_response",
224 # web_routedef
225 "AbstractRouteDef",
226 "RouteDef",
227 "RouteTableDef",
228 "StaticDef",
229 "delete",
230 "get",
231 "head",
232 "options",
233 "patch",
234 "post",
235 "put",
236 "route",
237 "static",
238 "view",
239 # web_runner
240 "AppRunner",
241 "BaseRunner",
242 "BaseSite",
243 "GracefulExit",
244 "ServerRunner",
245 "SockSite",
246 "TCPSite",
247 "UnixSite",
248 "NamedPipeSite",
249 # web_server
250 "Server",
251 # web_urldispatcher
252 "AbstractResource",
253 "AbstractRoute",
254 "DynamicResource",
255 "PlainResource",
256 "PrefixedSubAppResource",
257 "Resource",
258 "ResourceRoute",
259 "StaticResource",
260 "UrlDispatcher",
261 "UrlMappingMatchInfo",
262 "View",
263 # web_ws
264 "WebSocketReady",
265 "WebSocketResponse",
266 "WSMsgType",
267 # web
268 "run_app",
269)
272try:
273 from ssl import SSLContext
274except ImportError: # pragma: no cover
275 SSLContext = Any # type: ignore[misc,assignment]
277# Only display warning when using -Wdefault, -We, -X dev or similar.
278warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True)
280HostSequence = TypingIterable[str]
283async def _run_app(
284 app: Union[Application, Awaitable[Application]],
285 *,
286 host: Optional[Union[str, HostSequence]] = None,
287 port: Optional[int] = None,
288 path: Union[PathLike, TypingIterable[PathLike], None] = None,
289 sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None,
290 shutdown_timeout: float = 60.0,
291 keepalive_timeout: float = 75.0,
292 ssl_context: Optional[SSLContext] = None,
293 print: Optional[Callable[..., None]] = print,
294 backlog: int = 128,
295 access_log_class: Type[AbstractAccessLogger] = AccessLogger,
296 access_log_format: str = AccessLogger.LOG_FORMAT,
297 access_log: Optional[logging.Logger] = access_logger,
298 handle_signals: bool = True,
299 reuse_address: Optional[bool] = None,
300 reuse_port: Optional[bool] = None,
301 handler_cancellation: bool = False,
302) -> None:
303 async def wait(
304 starting_tasks: "WeakSet[asyncio.Task[object]]", shutdown_timeout: float
305 ) -> None:
306 # Wait for pending tasks for a given time limit.
307 t = asyncio.current_task()
308 assert t is not None
309 starting_tasks.add(t)
310 with suppress(asyncio.TimeoutError):
311 await asyncio.wait_for(_wait(starting_tasks), timeout=shutdown_timeout)
313 async def _wait(exclude: "WeakSet[asyncio.Task[object]]") -> None:
314 t = asyncio.current_task()
315 assert t is not None
316 exclude.add(t)
317 while tasks := asyncio.all_tasks().difference(exclude):
318 await asyncio.wait(tasks)
320 # An internal function to actually do all dirty job for application running
321 if asyncio.iscoroutine(app):
322 app = await app
324 app = cast(Application, app)
326 runner = AppRunner(
327 app,
328 handle_signals=handle_signals,
329 access_log_class=access_log_class,
330 access_log_format=access_log_format,
331 access_log=access_log,
332 keepalive_timeout=keepalive_timeout,
333 shutdown_timeout=shutdown_timeout,
334 handler_cancellation=handler_cancellation,
335 )
337 await runner.setup()
338 # On shutdown we want to avoid waiting on tasks which run forever.
339 # It's very likely that all tasks which run forever will have been created by
340 # the time we have completed the application startup (in runner.setup()),
341 # so we just record all running tasks here and exclude them later.
342 starting_tasks: "WeakSet[asyncio.Task[object]]" = WeakSet(asyncio.all_tasks())
343 runner.shutdown_callback = partial(wait, starting_tasks, shutdown_timeout)
345 sites: List[BaseSite] = []
347 try:
348 if host is not None:
349 if isinstance(host, (str, bytes, bytearray, memoryview)):
350 sites.append(
351 TCPSite(
352 runner,
353 host,
354 port,
355 ssl_context=ssl_context,
356 backlog=backlog,
357 reuse_address=reuse_address,
358 reuse_port=reuse_port,
359 )
360 )
361 else:
362 for h in host:
363 sites.append(
364 TCPSite(
365 runner,
366 h,
367 port,
368 ssl_context=ssl_context,
369 backlog=backlog,
370 reuse_address=reuse_address,
371 reuse_port=reuse_port,
372 )
373 )
374 elif path is None and sock is None or port is not None:
375 sites.append(
376 TCPSite(
377 runner,
378 port=port,
379 ssl_context=ssl_context,
380 backlog=backlog,
381 reuse_address=reuse_address,
382 reuse_port=reuse_port,
383 )
384 )
386 if path is not None:
387 if isinstance(path, (str, os.PathLike)):
388 sites.append(
389 UnixSite(
390 runner,
391 path,
392 ssl_context=ssl_context,
393 backlog=backlog,
394 )
395 )
396 else:
397 for p in path:
398 sites.append(
399 UnixSite(
400 runner,
401 p,
402 ssl_context=ssl_context,
403 backlog=backlog,
404 )
405 )
407 if sock is not None:
408 if not isinstance(sock, Iterable):
409 sites.append(
410 SockSite(
411 runner,
412 sock,
413 ssl_context=ssl_context,
414 backlog=backlog,
415 )
416 )
417 else:
418 for s in sock:
419 sites.append(
420 SockSite(
421 runner,
422 s,
423 ssl_context=ssl_context,
424 backlog=backlog,
425 )
426 )
427 for site in sites:
428 await site.start()
430 if print: # pragma: no branch
431 names = sorted(str(s.name) for s in runner.sites)
432 print(
433 "======== Running on {} ========\n"
434 "(Press CTRL+C to quit)".format(", ".join(names))
435 )
437 # sleep forever by 1 hour intervals,
438 while True:
439 await asyncio.sleep(3600)
440 finally:
441 await runner.cleanup()
444def _cancel_tasks(
445 to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop
446) -> None:
447 if not to_cancel:
448 return
450 for task in to_cancel:
451 task.cancel()
453 loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
455 for task in to_cancel:
456 if task.cancelled():
457 continue
458 if task.exception() is not None:
459 loop.call_exception_handler(
460 {
461 "message": "unhandled exception during asyncio.run() shutdown",
462 "exception": task.exception(),
463 "task": task,
464 }
465 )
468def run_app(
469 app: Union[Application, Awaitable[Application]],
470 *,
471 debug: bool = False,
472 host: Optional[Union[str, HostSequence]] = None,
473 port: Optional[int] = None,
474 path: Union[PathLike, TypingIterable[PathLike], None] = None,
475 sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None,
476 shutdown_timeout: float = 60.0,
477 keepalive_timeout: float = 75.0,
478 ssl_context: Optional[SSLContext] = None,
479 print: Optional[Callable[..., None]] = print,
480 backlog: int = 128,
481 access_log_class: Type[AbstractAccessLogger] = AccessLogger,
482 access_log_format: str = AccessLogger.LOG_FORMAT,
483 access_log: Optional[logging.Logger] = access_logger,
484 handle_signals: bool = True,
485 reuse_address: Optional[bool] = None,
486 reuse_port: Optional[bool] = None,
487 handler_cancellation: bool = False,
488 loop: Optional[asyncio.AbstractEventLoop] = None,
489) -> None:
490 """Run an app locally"""
491 if loop is None:
492 loop = asyncio.new_event_loop()
493 loop.set_debug(debug)
495 # Configure if and only if in debugging mode and using the default logger
496 if loop.get_debug() and access_log and access_log.name == "aiohttp.access":
497 if access_log.level == logging.NOTSET:
498 access_log.setLevel(logging.DEBUG)
499 if not access_log.hasHandlers():
500 access_log.addHandler(logging.StreamHandler())
502 main_task = loop.create_task(
503 _run_app(
504 app,
505 host=host,
506 port=port,
507 path=path,
508 sock=sock,
509 shutdown_timeout=shutdown_timeout,
510 keepalive_timeout=keepalive_timeout,
511 ssl_context=ssl_context,
512 print=print,
513 backlog=backlog,
514 access_log_class=access_log_class,
515 access_log_format=access_log_format,
516 access_log=access_log,
517 handle_signals=handle_signals,
518 reuse_address=reuse_address,
519 reuse_port=reuse_port,
520 handler_cancellation=handler_cancellation,
521 )
522 )
524 try:
525 asyncio.set_event_loop(loop)
526 loop.run_until_complete(main_task)
527 except (GracefulExit, KeyboardInterrupt): # pragma: no cover
528 pass
529 finally:
530 _cancel_tasks({main_task}, loop)
531 _cancel_tasks(asyncio.all_tasks(loop), loop)
532 loop.run_until_complete(loop.shutdown_asyncgens())
533 loop.close()
534 asyncio.set_event_loop(None)
537def main(argv: List[str]) -> None:
538 arg_parser = ArgumentParser(
539 description="aiohttp.web Application server", prog="aiohttp.web"
540 )
541 arg_parser.add_argument(
542 "entry_func",
543 help=(
544 "Callable returning the `aiohttp.web.Application` instance to "
545 "run. Should be specified in the 'module:function' syntax."
546 ),
547 metavar="entry-func",
548 )
549 arg_parser.add_argument(
550 "-H",
551 "--hostname",
552 help="TCP/IP hostname to serve on (default: %(default)r)",
553 default="localhost",
554 )
555 arg_parser.add_argument(
556 "-P",
557 "--port",
558 help="TCP/IP port to serve on (default: %(default)r)",
559 type=int,
560 default="8080",
561 )
562 arg_parser.add_argument(
563 "-U",
564 "--path",
565 help="Unix file system path to serve on. Specifying a path will cause "
566 "hostname and port arguments to be ignored.",
567 )
568 args, extra_argv = arg_parser.parse_known_args(argv)
570 # Import logic
571 mod_str, _, func_str = args.entry_func.partition(":")
572 if not func_str or not mod_str:
573 arg_parser.error("'entry-func' not in 'module:function' syntax")
574 if mod_str.startswith("."):
575 arg_parser.error("relative module names not supported")
576 try:
577 module = import_module(mod_str)
578 except ImportError as ex:
579 arg_parser.error(f"unable to import {mod_str}: {ex}")
580 try:
581 func = getattr(module, func_str)
582 except AttributeError:
583 arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}")
585 # Compatibility logic
586 if args.path is not None and not hasattr(socket, "AF_UNIX"):
587 arg_parser.error(
588 "file system paths not supported by your operating" " environment"
589 )
591 logging.basicConfig(level=logging.DEBUG)
593 app = func(extra_argv)
594 run_app(app, host=args.hostname, port=args.port, path=args.path)
595 arg_parser.exit(message="Stopped\n")
598if __name__ == "__main__": # pragma: no branch
599 main(sys.argv[1:]) # pragma: no cover