Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ctx.py450 linesDownload Raw Back to flask
1from __future__ import annotations2 3import contextvars4import sys5import typing as t6from functools import update_wrapper7from types import TracebackType8 9from werkzeug.exceptions import HTTPException10 11from . import typing as ft12from .globals import _cv_app13from .globals import _cv_request14from .signals import appcontext_popped15from .signals import appcontext_pushed16 17if t.TYPE_CHECKING:  # pragma: no cover18    from _typeshed.wsgi import WSGIEnvironment19 20    from .app import Flask21    from .sessions import SessionMixin22    from .wrappers import Request23 24 25# a singleton sentinel value for parameter defaults26_sentinel = object()27 28 29class _AppCtxGlobals:30    """A plain object. Used as a namespace for storing data during an31    application context.32 33    Creating an app context automatically creates this object, which is34    made available as the :data:`g` proxy.35 36    .. describe:: 'key' in g37 38        Check whether an attribute is present.39 40        .. versionadded:: 0.1041 42    .. describe:: iter(g)43 44        Return an iterator over the attribute names.45 46        .. versionadded:: 0.1047    """48 49    # Define attr methods to let mypy know this is a namespace object50    # that has arbitrary attributes.51 52    def __getattr__(self, name: str) -> t.Any:53        try:54            return self.__dict__[name]55        except KeyError:56            raise AttributeError(name) from None57 58    def __setattr__(self, name: str, value: t.Any) -> None:59        self.__dict__[name] = value60 61    def __delattr__(self, name: str) -> None:62        try:63            del self.__dict__[name]64        except KeyError:65            raise AttributeError(name) from None66 67    def get(self, name: str, default: t.Any | None = None) -> t.Any:68        """Get an attribute by name, or a default value. Like69        :meth:`dict.get`.70 71        :param name: Name of attribute to get.72        :param default: Value to return if the attribute is not present.73 74        .. versionadded:: 0.1075        """76        return self.__dict__.get(name, default)77 78    def pop(self, name: str, default: t.Any = _sentinel) -> t.Any:79        """Get and remove an attribute by name. Like :meth:`dict.pop`.80 81        :param name: Name of attribute to pop.82        :param default: Value to return if the attribute is not present,83            instead of raising a ``KeyError``.84 85        .. versionadded:: 0.1186        """87        if default is _sentinel:88            return self.__dict__.pop(name)89        else:90            return self.__dict__.pop(name, default)91 92    def setdefault(self, name: str, default: t.Any = None) -> t.Any:93        """Get the value of an attribute if it is present, otherwise94        set and return a default value. Like :meth:`dict.setdefault`.95 96        :param name: Name of attribute to get.97        :param default: Value to set and return if the attribute is not98            present.99 100        .. versionadded:: 0.11101        """102        return self.__dict__.setdefault(name, default)103 104    def __contains__(self, item: str) -> bool:105        return item in self.__dict__106 107    def __iter__(self) -> t.Iterator[str]:108        return iter(self.__dict__)109 110    def __repr__(self) -> str:111        ctx = _cv_app.get(None)112        if ctx is not None:113            return f"<flask.g of '{ctx.app.name}'>"114        return object.__repr__(self)115 116 117def after_this_request(118    f: ft.AfterRequestCallable[t.Any],119) -> ft.AfterRequestCallable[t.Any]:120    """Executes a function after this request.  This is useful to modify121    response objects.  The function is passed the response object and has122    to return the same or a new one.123 124    Example::125 126        @app.route('/')127        def index():128            @after_this_request129            def add_header(response):130                response.headers['X-Foo'] = 'Parachute'131                return response132            return 'Hello World!'133 134    This is more useful if a function other than the view function wants to135    modify a response.  For instance think of a decorator that wants to add136    some headers without converting the return value into a response object.137 138    .. versionadded:: 0.9139    """140    ctx = _cv_request.get(None)141 142    if ctx is None:143        raise RuntimeError(144            "'after_this_request' can only be used when a request"145            " context is active, such as in a view function."146        )147 148    ctx._after_request_functions.append(f)149    return f150 151 152F = t.TypeVar("F", bound=t.Callable[..., t.Any])153 154 155def copy_current_request_context(f: F) -> F:156    """A helper function that decorates a function to retain the current157    request context.  This is useful when working with greenlets.  The moment158    the function is decorated a copy of the request context is created and159    then pushed when the function is called.  The current session is also160    included in the copied request context.161 162    Example::163 164        import gevent165        from flask import copy_current_request_context166 167        @app.route('/')168        def index():169            @copy_current_request_context170            def do_some_work():171                # do some work here, it can access flask.request or172                # flask.session like you would otherwise in the view function.173                ...174            gevent.spawn(do_some_work)175            return 'Regular response'176 177    .. versionadded:: 0.10178    """179    ctx = _cv_request.get(None)180 181    if ctx is None:182        raise RuntimeError(183            "'copy_current_request_context' can only be used when a"184            " request context is active, such as in a view function."185        )186 187    ctx = ctx.copy()188 189    def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:190        with ctx:  # type: ignore[union-attr]191            return ctx.app.ensure_sync(f)(*args, **kwargs)  # type: ignore[union-attr]192 193    return update_wrapper(wrapper, f)  # type: ignore[return-value]194 195 196def has_request_context() -> bool:197    """If you have code that wants to test if a request context is there or198    not this function can be used.  For instance, you may want to take advantage199    of request information if the request object is available, but fail200    silently if it is unavailable.201 202    ::203 204        class User(db.Model):205 206            def __init__(self, username, remote_addr=None):207                self.username = username208                if remote_addr is None and has_request_context():209                    remote_addr = request.remote_addr210                self.remote_addr = remote_addr211 212    Alternatively you can also just test any of the context bound objects213    (such as :class:`request` or :class:`g`) for truthness::214 215        class User(db.Model):216 217            def __init__(self, username, remote_addr=None):218                self.username = username219                if remote_addr is None and request:220                    remote_addr = request.remote_addr221                self.remote_addr = remote_addr222 223    .. versionadded:: 0.7224    """225    return _cv_request.get(None) is not None226 227 228def has_app_context() -> bool:229    """Works like :func:`has_request_context` but for the application230    context.  You can also just do a boolean check on the231    :data:`current_app` object instead.232 233    .. versionadded:: 0.9234    """235    return _cv_app.get(None) is not None236 237 238class AppContext:239    """The app context contains application-specific information. An app240    context is created and pushed at the beginning of each request if241    one is not already active. An app context is also pushed when242    running CLI commands.243    """244 245    def __init__(self, app: Flask) -> None:246        self.app = app247        self.url_adapter = app.create_url_adapter(None)248        self.g: _AppCtxGlobals = app.app_ctx_globals_class()249        self._cv_tokens: list[contextvars.Token[AppContext]] = []250 251    def push(self) -> None:252        """Binds the app context to the current context."""253        self._cv_tokens.append(_cv_app.set(self))254        appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)255 256    def pop(self, exc: BaseException | None = _sentinel) -> None:  # type: ignore257        """Pops the app context."""258        try:259            if len(self._cv_tokens) == 1:260                if exc is _sentinel:261                    exc = sys.exc_info()[1]262                self.app.do_teardown_appcontext(exc)263        finally:264            ctx = _cv_app.get()265            _cv_app.reset(self._cv_tokens.pop())266 267        if ctx is not self:268            raise AssertionError(269                f"Popped wrong app context. ({ctx!r} instead of {self!r})"270            )271 272        appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync)273 274    def __enter__(self) -> AppContext:275        self.push()276        return self277 278    def __exit__(279        self,280        exc_type: type | None,281        exc_value: BaseException | None,282        tb: TracebackType | None,283    ) -> None:284        self.pop(exc_value)285 286 287class RequestContext:288    """The request context contains per-request information. The Flask289    app creates and pushes it at the beginning of the request, then pops290    it at the end of the request. It will create the URL adapter and291    request object for the WSGI environment provided.292 293    Do not attempt to use this class directly, instead use294    :meth:`~flask.Flask.test_request_context` and295    :meth:`~flask.Flask.request_context` to create this object.296 297    When the request context is popped, it will evaluate all the298    functions registered on the application for teardown execution299    (:meth:`~flask.Flask.teardown_request`).300 301    The request context is automatically popped at the end of the302    request. When using the interactive debugger, the context will be303    restored so ``request`` is still accessible. Similarly, the test304    client can preserve the context after the request ends. However,305    teardown functions may already have closed some resources such as306    database connections.307    """308 309    def __init__(310        self,311        app: Flask,312        environ: WSGIEnvironment,313        request: Request | None = None,314        session: SessionMixin | None = None,315    ) -> None:316        self.app = app317        if request is None:318            request = app.request_class(environ)319            request.json_module = app.json320        self.request: Request = request321        self.url_adapter = None322        try:323            self.url_adapter = app.create_url_adapter(self.request)324        except HTTPException as e:325            self.request.routing_exception = e326        self.flashes: list[tuple[str, str]] | None = None327        self.session: SessionMixin | None = session328        # Functions that should be executed after the request on the response329        # object.  These will be called before the regular "after_request"330        # functions.331        self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = []332 333        self._cv_tokens: list[334            tuple[contextvars.Token[RequestContext], AppContext | None]335        ] = []336 337    def copy(self) -> RequestContext:338        """Creates a copy of this request context with the same request object.339        This can be used to move a request context to a different greenlet.340        Because the actual request object is the same this cannot be used to341        move a request context to a different thread unless access to the342        request object is locked.343 344        .. versionadded:: 0.10345 346        .. versionchanged:: 1.1347           The current session object is used instead of reloading the original348           data. This prevents `flask.session` pointing to an out-of-date object.349        """350        return self.__class__(351            self.app,352            environ=self.request.environ,353            request=self.request,354            session=self.session,355        )356 357    def match_request(self) -> None:358        """Can be overridden by a subclass to hook into the matching359        of the request.360        """361        try:362            result = self.url_adapter.match(return_rule=True)  # type: ignore363            self.request.url_rule, self.request.view_args = result  # type: ignore364        except HTTPException as e:365            self.request.routing_exception = e366 367    def push(self) -> None:368        # Before we push the request context we have to ensure that there369        # is an application context.370        app_ctx = _cv_app.get(None)371 372        if app_ctx is None or app_ctx.app is not self.app:373            app_ctx = self.app.app_context()374            app_ctx.push()375        else:376            app_ctx = None377 378        self._cv_tokens.append((_cv_request.set(self), app_ctx))379 380        # Open the session at the moment that the request context is available.381        # This allows a custom open_session method to use the request context.382        # Only open a new session if this is the first time the request was383        # pushed, otherwise stream_with_context loses the session.384        if self.session is None:385            session_interface = self.app.session_interface386            self.session = session_interface.open_session(self.app, self.request)387 388            if self.session is None:389                self.session = session_interface.make_null_session(self.app)390 391        # Match the request URL after loading the session, so that the392        # session is available in custom URL converters.393        if self.url_adapter is not None:394            self.match_request()395 396    def pop(self, exc: BaseException | None = _sentinel) -> None:  # type: ignore397        """Pops the request context and unbinds it by doing that.  This will398        also trigger the execution of functions registered by the399        :meth:`~flask.Flask.teardown_request` decorator.400 401        .. versionchanged:: 0.9402           Added the `exc` argument.403        """404        clear_request = len(self._cv_tokens) == 1405 406        try:407            if clear_request:408                if exc is _sentinel:409                    exc = sys.exc_info()[1]410                self.app.do_teardown_request(exc)411 412                request_close = getattr(self.request, "close", None)413                if request_close is not None:414                    request_close()415        finally:416            ctx = _cv_request.get()417            token, app_ctx = self._cv_tokens.pop()418            _cv_request.reset(token)419 420            # get rid of circular dependencies at the end of the request421            # so that we don't require the GC to be active.422            if clear_request:423                ctx.request.environ["werkzeug.request"] = None424 425            if app_ctx is not None:426                app_ctx.pop(exc)427 428            if ctx is not self:429                raise AssertionError(430                    f"Popped wrong request context. ({ctx!r} instead of {self!r})"431                )432 433    def __enter__(self) -> RequestContext:434        self.push()435        return self436 437    def __exit__(438        self,439        exc_type: type | None,440        exc_value: BaseException | None,441        tb: TracebackType | None,442    ) -> None:443        self.pop(exc_value)444 445    def __repr__(self) -> str:446        return (447            f"<{type(self).__name__} {self.request.url!r}"448            f" [{self.request.method}] of {self.app.name}>"449        )450 
codekingpro/portable-devtools · Team Ai