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