Team Ai
Datasetpublic

codekingpro/portable-devtools

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