| | import asyncio |
| | import logging |
| | import os |
| | import socket |
| | import sys |
| | import warnings |
| | from argparse import ArgumentParser |
| | from collections.abc import Iterable |
| | from contextlib import suppress |
| | from importlib import import_module |
| | from typing import ( |
| | TYPE_CHECKING, |
| | Any, |
| | Awaitable, |
| | Callable, |
| | Iterable as TypingIterable, |
| | List, |
| | Optional, |
| | Set, |
| | Type, |
| | Union, |
| | cast, |
| | ) |
| |
|
| | from .abc import AbstractAccessLogger |
| | from .helpers import AppKey as AppKey |
| | from .log import access_logger |
| | from .typedefs import PathLike |
| | from .web_app import Application as Application, CleanupError as CleanupError |
| | from .web_exceptions import ( |
| | HTTPAccepted as HTTPAccepted, |
| | HTTPBadGateway as HTTPBadGateway, |
| | HTTPBadRequest as HTTPBadRequest, |
| | HTTPClientError as HTTPClientError, |
| | HTTPConflict as HTTPConflict, |
| | HTTPCreated as HTTPCreated, |
| | HTTPError as HTTPError, |
| | HTTPException as HTTPException, |
| | HTTPExpectationFailed as HTTPExpectationFailed, |
| | HTTPFailedDependency as HTTPFailedDependency, |
| | HTTPForbidden as HTTPForbidden, |
| | HTTPFound as HTTPFound, |
| | HTTPGatewayTimeout as HTTPGatewayTimeout, |
| | HTTPGone as HTTPGone, |
| | HTTPInsufficientStorage as HTTPInsufficientStorage, |
| | HTTPInternalServerError as HTTPInternalServerError, |
| | HTTPLengthRequired as HTTPLengthRequired, |
| | HTTPMethodNotAllowed as HTTPMethodNotAllowed, |
| | HTTPMisdirectedRequest as HTTPMisdirectedRequest, |
| | HTTPMove as HTTPMove, |
| | HTTPMovedPermanently as HTTPMovedPermanently, |
| | HTTPMultipleChoices as HTTPMultipleChoices, |
| | HTTPNetworkAuthenticationRequired as HTTPNetworkAuthenticationRequired, |
| | HTTPNoContent as HTTPNoContent, |
| | HTTPNonAuthoritativeInformation as HTTPNonAuthoritativeInformation, |
| | HTTPNotAcceptable as HTTPNotAcceptable, |
| | HTTPNotExtended as HTTPNotExtended, |
| | HTTPNotFound as HTTPNotFound, |
| | HTTPNotImplemented as HTTPNotImplemented, |
| | HTTPNotModified as HTTPNotModified, |
| | HTTPOk as HTTPOk, |
| | HTTPPartialContent as HTTPPartialContent, |
| | HTTPPaymentRequired as HTTPPaymentRequired, |
| | HTTPPermanentRedirect as HTTPPermanentRedirect, |
| | HTTPPreconditionFailed as HTTPPreconditionFailed, |
| | HTTPPreconditionRequired as HTTPPreconditionRequired, |
| | HTTPProxyAuthenticationRequired as HTTPProxyAuthenticationRequired, |
| | HTTPRedirection as HTTPRedirection, |
| | HTTPRequestEntityTooLarge as HTTPRequestEntityTooLarge, |
| | HTTPRequestHeaderFieldsTooLarge as HTTPRequestHeaderFieldsTooLarge, |
| | HTTPRequestRangeNotSatisfiable as HTTPRequestRangeNotSatisfiable, |
| | HTTPRequestTimeout as HTTPRequestTimeout, |
| | HTTPRequestURITooLong as HTTPRequestURITooLong, |
| | HTTPResetContent as HTTPResetContent, |
| | HTTPSeeOther as HTTPSeeOther, |
| | HTTPServerError as HTTPServerError, |
| | HTTPServiceUnavailable as HTTPServiceUnavailable, |
| | HTTPSuccessful as HTTPSuccessful, |
| | HTTPTemporaryRedirect as HTTPTemporaryRedirect, |
| | HTTPTooManyRequests as HTTPTooManyRequests, |
| | HTTPUnauthorized as HTTPUnauthorized, |
| | HTTPUnavailableForLegalReasons as HTTPUnavailableForLegalReasons, |
| | HTTPUnprocessableEntity as HTTPUnprocessableEntity, |
| | HTTPUnsupportedMediaType as HTTPUnsupportedMediaType, |
| | HTTPUpgradeRequired as HTTPUpgradeRequired, |
| | HTTPUseProxy as HTTPUseProxy, |
| | HTTPVariantAlsoNegotiates as HTTPVariantAlsoNegotiates, |
| | HTTPVersionNotSupported as HTTPVersionNotSupported, |
| | NotAppKeyWarning as NotAppKeyWarning, |
| | ) |
| | from .web_fileresponse import FileResponse as FileResponse |
| | from .web_log import AccessLogger |
| | from .web_middlewares import ( |
| | middleware as middleware, |
| | normalize_path_middleware as normalize_path_middleware, |
| | ) |
| | from .web_protocol import ( |
| | PayloadAccessError as PayloadAccessError, |
| | RequestHandler as RequestHandler, |
| | RequestPayloadError as RequestPayloadError, |
| | ) |
| | from .web_request import ( |
| | BaseRequest as BaseRequest, |
| | FileField as FileField, |
| | Request as Request, |
| | ) |
| | from .web_response import ( |
| | ContentCoding as ContentCoding, |
| | Response as Response, |
| | StreamResponse as StreamResponse, |
| | json_response as json_response, |
| | ) |
| | from .web_routedef import ( |
| | AbstractRouteDef as AbstractRouteDef, |
| | RouteDef as RouteDef, |
| | RouteTableDef as RouteTableDef, |
| | StaticDef as StaticDef, |
| | delete as delete, |
| | get as get, |
| | head as head, |
| | options as options, |
| | patch as patch, |
| | post as post, |
| | put as put, |
| | route as route, |
| | static as static, |
| | view as view, |
| | ) |
| | from .web_runner import ( |
| | AppRunner as AppRunner, |
| | BaseRunner as BaseRunner, |
| | BaseSite as BaseSite, |
| | GracefulExit as GracefulExit, |
| | NamedPipeSite as NamedPipeSite, |
| | ServerRunner as ServerRunner, |
| | SockSite as SockSite, |
| | TCPSite as TCPSite, |
| | UnixSite as UnixSite, |
| | ) |
| | from .web_server import Server as Server |
| | from .web_urldispatcher import ( |
| | AbstractResource as AbstractResource, |
| | AbstractRoute as AbstractRoute, |
| | DynamicResource as DynamicResource, |
| | PlainResource as PlainResource, |
| | PrefixedSubAppResource as PrefixedSubAppResource, |
| | Resource as Resource, |
| | ResourceRoute as ResourceRoute, |
| | StaticResource as StaticResource, |
| | UrlDispatcher as UrlDispatcher, |
| | UrlMappingMatchInfo as UrlMappingMatchInfo, |
| | View as View, |
| | ) |
| | from .web_ws import ( |
| | WebSocketReady as WebSocketReady, |
| | WebSocketResponse as WebSocketResponse, |
| | WSMsgType as WSMsgType, |
| | ) |
| |
|
| | __all__ = ( |
| | |
| | "AppKey", |
| | "Application", |
| | "CleanupError", |
| | |
| | "NotAppKeyWarning", |
| | "HTTPAccepted", |
| | "HTTPBadGateway", |
| | "HTTPBadRequest", |
| | "HTTPClientError", |
| | "HTTPConflict", |
| | "HTTPCreated", |
| | "HTTPError", |
| | "HTTPException", |
| | "HTTPExpectationFailed", |
| | "HTTPFailedDependency", |
| | "HTTPForbidden", |
| | "HTTPFound", |
| | "HTTPGatewayTimeout", |
| | "HTTPGone", |
| | "HTTPInsufficientStorage", |
| | "HTTPInternalServerError", |
| | "HTTPLengthRequired", |
| | "HTTPMethodNotAllowed", |
| | "HTTPMisdirectedRequest", |
| | "HTTPMove", |
| | "HTTPMovedPermanently", |
| | "HTTPMultipleChoices", |
| | "HTTPNetworkAuthenticationRequired", |
| | "HTTPNoContent", |
| | "HTTPNonAuthoritativeInformation", |
| | "HTTPNotAcceptable", |
| | "HTTPNotExtended", |
| | "HTTPNotFound", |
| | "HTTPNotImplemented", |
| | "HTTPNotModified", |
| | "HTTPOk", |
| | "HTTPPartialContent", |
| | "HTTPPaymentRequired", |
| | "HTTPPermanentRedirect", |
| | "HTTPPreconditionFailed", |
| | "HTTPPreconditionRequired", |
| | "HTTPProxyAuthenticationRequired", |
| | "HTTPRedirection", |
| | "HTTPRequestEntityTooLarge", |
| | "HTTPRequestHeaderFieldsTooLarge", |
| | "HTTPRequestRangeNotSatisfiable", |
| | "HTTPRequestTimeout", |
| | "HTTPRequestURITooLong", |
| | "HTTPResetContent", |
| | "HTTPSeeOther", |
| | "HTTPServerError", |
| | "HTTPServiceUnavailable", |
| | "HTTPSuccessful", |
| | "HTTPTemporaryRedirect", |
| | "HTTPTooManyRequests", |
| | "HTTPUnauthorized", |
| | "HTTPUnavailableForLegalReasons", |
| | "HTTPUnprocessableEntity", |
| | "HTTPUnsupportedMediaType", |
| | "HTTPUpgradeRequired", |
| | "HTTPUseProxy", |
| | "HTTPVariantAlsoNegotiates", |
| | "HTTPVersionNotSupported", |
| | |
| | "FileResponse", |
| | |
| | "middleware", |
| | "normalize_path_middleware", |
| | |
| | "PayloadAccessError", |
| | "RequestHandler", |
| | "RequestPayloadError", |
| | |
| | "BaseRequest", |
| | "FileField", |
| | "Request", |
| | |
| | "ContentCoding", |
| | "Response", |
| | "StreamResponse", |
| | "json_response", |
| | |
| | "AbstractRouteDef", |
| | "RouteDef", |
| | "RouteTableDef", |
| | "StaticDef", |
| | "delete", |
| | "get", |
| | "head", |
| | "options", |
| | "patch", |
| | "post", |
| | "put", |
| | "route", |
| | "static", |
| | "view", |
| | |
| | "AppRunner", |
| | "BaseRunner", |
| | "BaseSite", |
| | "GracefulExit", |
| | "ServerRunner", |
| | "SockSite", |
| | "TCPSite", |
| | "UnixSite", |
| | "NamedPipeSite", |
| | |
| | "Server", |
| | |
| | "AbstractResource", |
| | "AbstractRoute", |
| | "DynamicResource", |
| | "PlainResource", |
| | "PrefixedSubAppResource", |
| | "Resource", |
| | "ResourceRoute", |
| | "StaticResource", |
| | "UrlDispatcher", |
| | "UrlMappingMatchInfo", |
| | "View", |
| | |
| | "WebSocketReady", |
| | "WebSocketResponse", |
| | "WSMsgType", |
| | |
| | "run_app", |
| | ) |
| |
|
| |
|
| | if TYPE_CHECKING: |
| | from ssl import SSLContext |
| | else: |
| | try: |
| | from ssl import SSLContext |
| | except ImportError: |
| | SSLContext = object |
| |
|
| | |
| | warnings.filterwarnings("ignore", category=NotAppKeyWarning, append=True) |
| |
|
| | HostSequence = TypingIterable[str] |
| |
|
| |
|
| | async def _run_app( |
| | app: Union[Application, Awaitable[Application]], |
| | *, |
| | host: Optional[Union[str, HostSequence]] = None, |
| | port: Optional[int] = None, |
| | path: Union[PathLike, TypingIterable[PathLike], None] = None, |
| | sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, |
| | ssl_context: Optional[SSLContext] = None, |
| | print: Optional[Callable[..., None]] = print, |
| | backlog: int = 128, |
| | reuse_address: Optional[bool] = None, |
| | reuse_port: Optional[bool] = None, |
| | **kwargs: Any, |
| | ) -> None: |
| | |
| | if asyncio.iscoroutine(app): |
| | app = await app |
| |
|
| | app = cast(Application, app) |
| |
|
| | runner = AppRunner(app, **kwargs) |
| |
|
| | await runner.setup() |
| |
|
| | sites: List[BaseSite] = [] |
| |
|
| | try: |
| | if host is not None: |
| | if isinstance(host, str): |
| | sites.append( |
| | TCPSite( |
| | runner, |
| | host, |
| | port, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | reuse_address=reuse_address, |
| | reuse_port=reuse_port, |
| | ) |
| | ) |
| | else: |
| | for h in host: |
| | sites.append( |
| | TCPSite( |
| | runner, |
| | h, |
| | port, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | reuse_address=reuse_address, |
| | reuse_port=reuse_port, |
| | ) |
| | ) |
| | elif path is None and sock is None or port is not None: |
| | sites.append( |
| | TCPSite( |
| | runner, |
| | port=port, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | reuse_address=reuse_address, |
| | reuse_port=reuse_port, |
| | ) |
| | ) |
| |
|
| | if path is not None: |
| | if isinstance(path, (str, os.PathLike)): |
| | sites.append( |
| | UnixSite( |
| | runner, |
| | path, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | ) |
| | ) |
| | else: |
| | for p in path: |
| | sites.append( |
| | UnixSite( |
| | runner, |
| | p, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | ) |
| | ) |
| |
|
| | if sock is not None: |
| | if not isinstance(sock, Iterable): |
| | sites.append( |
| | SockSite( |
| | runner, |
| | sock, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | ) |
| | ) |
| | else: |
| | for s in sock: |
| | sites.append( |
| | SockSite( |
| | runner, |
| | s, |
| | ssl_context=ssl_context, |
| | backlog=backlog, |
| | ) |
| | ) |
| | for site in sites: |
| | await site.start() |
| |
|
| | if print: |
| | names = sorted(str(s.name) for s in runner.sites) |
| | print( |
| | "======== Running on {} ========\n" |
| | "(Press CTRL+C to quit)".format(", ".join(names)) |
| | ) |
| |
|
| | |
| | while True: |
| | await asyncio.sleep(3600) |
| | finally: |
| | await runner.cleanup() |
| |
|
| |
|
| | def _cancel_tasks( |
| | to_cancel: Set["asyncio.Task[Any]"], loop: asyncio.AbstractEventLoop |
| | ) -> None: |
| | if not to_cancel: |
| | return |
| |
|
| | for task in to_cancel: |
| | task.cancel() |
| |
|
| | loop.run_until_complete(asyncio.gather(*to_cancel, return_exceptions=True)) |
| |
|
| | for task in to_cancel: |
| | if task.cancelled(): |
| | continue |
| | if task.exception() is not None: |
| | loop.call_exception_handler( |
| | { |
| | "message": "unhandled exception during asyncio.run() shutdown", |
| | "exception": task.exception(), |
| | "task": task, |
| | } |
| | ) |
| |
|
| |
|
| | def run_app( |
| | app: Union[Application, Awaitable[Application]], |
| | *, |
| | host: Optional[Union[str, HostSequence]] = None, |
| | port: Optional[int] = None, |
| | path: Union[PathLike, TypingIterable[PathLike], None] = None, |
| | sock: Optional[Union[socket.socket, TypingIterable[socket.socket]]] = None, |
| | shutdown_timeout: float = 60.0, |
| | keepalive_timeout: float = 75.0, |
| | ssl_context: Optional[SSLContext] = None, |
| | print: Optional[Callable[..., None]] = print, |
| | backlog: int = 128, |
| | access_log_class: Type[AbstractAccessLogger] = AccessLogger, |
| | access_log_format: str = AccessLogger.LOG_FORMAT, |
| | access_log: Optional[logging.Logger] = access_logger, |
| | handle_signals: bool = True, |
| | reuse_address: Optional[bool] = None, |
| | reuse_port: Optional[bool] = None, |
| | handler_cancellation: bool = False, |
| | loop: Optional[asyncio.AbstractEventLoop] = None, |
| | **kwargs: Any, |
| | ) -> None: |
| | """Run an app locally""" |
| | if loop is None: |
| | loop = asyncio.new_event_loop() |
| |
|
| | |
| | if loop.get_debug() and access_log and access_log.name == "aiohttp.access": |
| | if access_log.level == logging.NOTSET: |
| | access_log.setLevel(logging.DEBUG) |
| | if not access_log.hasHandlers(): |
| | access_log.addHandler(logging.StreamHandler()) |
| |
|
| | main_task = loop.create_task( |
| | _run_app( |
| | app, |
| | host=host, |
| | port=port, |
| | path=path, |
| | sock=sock, |
| | shutdown_timeout=shutdown_timeout, |
| | keepalive_timeout=keepalive_timeout, |
| | ssl_context=ssl_context, |
| | print=print, |
| | backlog=backlog, |
| | access_log_class=access_log_class, |
| | access_log_format=access_log_format, |
| | access_log=access_log, |
| | handle_signals=handle_signals, |
| | reuse_address=reuse_address, |
| | reuse_port=reuse_port, |
| | handler_cancellation=handler_cancellation, |
| | **kwargs, |
| | ) |
| | ) |
| |
|
| | try: |
| | asyncio.set_event_loop(loop) |
| | loop.run_until_complete(main_task) |
| | except (GracefulExit, KeyboardInterrupt): |
| | pass |
| | finally: |
| | try: |
| | main_task.cancel() |
| | with suppress(asyncio.CancelledError): |
| | loop.run_until_complete(main_task) |
| | finally: |
| | _cancel_tasks(asyncio.all_tasks(loop), loop) |
| | loop.run_until_complete(loop.shutdown_asyncgens()) |
| | loop.close() |
| |
|
| |
|
| | def main(argv: List[str]) -> None: |
| | arg_parser = ArgumentParser( |
| | description="aiohttp.web Application server", prog="aiohttp.web" |
| | ) |
| | arg_parser.add_argument( |
| | "entry_func", |
| | help=( |
| | "Callable returning the `aiohttp.web.Application` instance to " |
| | "run. Should be specified in the 'module:function' syntax." |
| | ), |
| | metavar="entry-func", |
| | ) |
| | arg_parser.add_argument( |
| | "-H", |
| | "--hostname", |
| | help="TCP/IP hostname to serve on (default: localhost)", |
| | default=None, |
| | ) |
| | arg_parser.add_argument( |
| | "-P", |
| | "--port", |
| | help="TCP/IP port to serve on (default: %(default)r)", |
| | type=int, |
| | default=8080, |
| | ) |
| | arg_parser.add_argument( |
| | "-U", |
| | "--path", |
| | help="Unix file system path to serve on. Can be combined with hostname " |
| | "to serve on both Unix and TCP.", |
| | ) |
| | args, extra_argv = arg_parser.parse_known_args(argv) |
| |
|
| | |
| | mod_str, _, func_str = args.entry_func.partition(":") |
| | if not func_str or not mod_str: |
| | arg_parser.error("'entry-func' not in 'module:function' syntax") |
| | if mod_str.startswith("."): |
| | arg_parser.error("relative module names not supported") |
| | try: |
| | module = import_module(mod_str) |
| | except ImportError as ex: |
| | arg_parser.error(f"unable to import {mod_str}: {ex}") |
| | try: |
| | func = getattr(module, func_str) |
| | except AttributeError: |
| | arg_parser.error(f"module {mod_str!r} has no attribute {func_str!r}") |
| |
|
| | |
| | if args.path is not None and not hasattr(socket, "AF_UNIX"): |
| | arg_parser.error( |
| | "file system paths not supported by your operating environment" |
| | ) |
| |
|
| | logging.basicConfig(level=logging.DEBUG) |
| |
|
| | if args.path and args.hostname is None: |
| | host = port = None |
| | else: |
| | host = args.hostname or "localhost" |
| | port = args.port |
| |
|
| | app = func(extra_argv) |
| | run_app(app, host=host, port=port, path=args.path) |
| | arg_parser.exit(message="Stopped\n") |
| |
|
| |
|
| | if __name__ == "__main__": |
| | main(sys.argv[1:]) |
| |
|