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
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
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)
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
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)
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]
279# Only display warning when using -Wdefault, -We, -X dev or similar.
280warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True)
282HostSequence = TypingIterable[str]
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
309 app = cast(Application, app)
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 )
322 await runner.setup()
324 sites: List[BaseSite] = []
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 )
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 )
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()
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 )
416 # sleep forever by 1 hour intervals,
417 while True:
418 await asyncio.sleep(3600)
419 finally:
420 await runner.cleanup()
423def _cancel_tasks(
424 to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop
425) -> None:
426 if not to_cancel:
427 return
429 for task in to_cancel:
430 task.cancel()
432 loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
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 )
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)
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())
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 )
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)
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)
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}")
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 )
574 logging.basicConfig(level=logging.DEBUG)
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
582 app = func(extra_argv)
583 run_app(app, host=host, port=port, path=args.path)
584 arg_parser.exit(message="Stopped\n")
587if __name__ == "__main__": # pragma: no branch
588 main(sys.argv[1:]) # pragma: no cover