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
« 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)
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
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)
266try:
267 from ssl import SSLContext
268except ImportError: # pragma: no cover
269 SSLContext = Any # type: ignore[misc,assignment]
271HostSequence = TypingIterable[str]
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
298 app = cast(Application, app)
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 )
310 await runner.setup()
312 sites: List[BaseSite] = []
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 )
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 )
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()
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 )
411 # sleep forever by 1 hour intervals,
412 while True:
413 await asyncio.sleep(3600)
414 finally:
415 await runner.cleanup()
418def _cancel_tasks(
419 to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop
420) -> None:
421 if not to_cancel:
422 return
424 for task in to_cancel:
425 task.cancel()
427 loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
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 )
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)
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())
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 )
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)
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)
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}")
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 )
565 logging.basicConfig(level=logging.DEBUG)
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")
572if __name__ == "__main__": # pragma: no branch
573 main(sys.argv[1:]) # pragma: no cover