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
« 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)
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 # 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
419 while True:
420 await asyncio.sleep(delay)
421 finally:
422 await runner.cleanup()
425def _cancel_tasks(
426 to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop
427) -> None:
428 if not to_cancel:
429 return
431 for task in to_cancel:
432 task.cancel()
434 loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True))
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 )
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)
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())
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 )
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)
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)
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}")
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 )
572 logging.basicConfig(level=logging.DEBUG)
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")
579if __name__ == "__main__": # pragma: no branch
580 main(sys.argv[1:]) # pragma: no cover