{
    "file_diffs": [
        {
            "old_file_content": "",
            "new_file_content": "Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`.\n",
            "header": {
                "file": {
                    "path": "CHANGES/8908.bugfix.rst"
                },
                "misc_line": "new file mode 100644"
            },
            "index_line": {
                "old_commit_hash": "00000000",
                "new_commit_hash": "0eb45043",
                "mode": ""
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "/dev/null"
            },
            "plus_file": {
                "path": "b/CHANGES/8908.bugfix.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 0,
                            "length": 0
                        },
                        "new_range": {
                            "start": 1,
                            "length": null
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`.",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport logging\nimport warnings\nfrom functools import partial, update_wrapper\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Awaitable,\n    Callable,\n    Dict,\n    Iterable,\n    Iterator,\n    List,\n    Mapping,\n    MutableMapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n    overload,\n)\n\nfrom aiosignal import Signal\nfrom frozenlist import FrozenList\n\nfrom . import hdrs\nfrom .abc import (\n    AbstractAccessLogger,\n    AbstractMatchInfo,\n    AbstractRouter,\n    AbstractStreamWriter,\n)\nfrom .helpers import DEBUG, AppKey\nfrom .http_parser import RawRequestMessage\nfrom .log import web_logger\nfrom .streams import StreamReader\nfrom .typedefs import Middleware\nfrom .web_exceptions import NotAppKeyWarning\nfrom .web_log import AccessLogger\nfrom .web_middlewares import _fix_request_current_app\nfrom .web_protocol import RequestHandler\nfrom .web_request import Request\nfrom .web_response import StreamResponse\nfrom .web_routedef import AbstractRouteDef\nfrom .web_server import Server\nfrom .web_urldispatcher import (\n    AbstractResource,\n    AbstractRoute,\n    Domain,\n    MaskDomain,\n    MatchedSubAppResource,\n    PrefixedSubAppResource,\n    UrlDispatcher,\n)\n\n__all__ = (\"Application\", \"CleanupError\")\n\n\nif TYPE_CHECKING:\n    _AppSignal = Signal[Callable[[\"Application\"], Awaitable[None]]]\n    _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]\n    _Middlewares = FrozenList[Middleware]\n    _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]]\n    _Subapps = List[\"Application\"]\nelse:\n    # No type checker mode, skip types\n    _AppSignal = Signal\n    _RespPrepareSignal = Signal\n    _Middlewares = FrozenList\n    _MiddlewaresHandlers = Optional[Sequence]\n    _Subapps = List\n\n_T = TypeVar(\"_T\")\n_U = TypeVar(\"_U\")\n_Resource = TypeVar(\"_Resource\", bound=AbstractResource)\n\n\nclass Application(MutableMapping[Union[str, AppKey[Any]], Any]):\n    ATTRS = frozenset(\n        [\n            \"logger\",\n            \"_debug\",\n            \"_router\",\n            \"_loop\",\n            \"_handler_args\",\n            \"_middlewares\",\n            \"_middlewares_handlers\",\n            \"_run_middlewares\",\n            \"_state\",\n            \"_frozen\",\n            \"_pre_frozen\",\n            \"_subapps\",\n            \"_on_response_prepare\",\n            \"_on_startup\",\n            \"_on_shutdown\",\n            \"_on_cleanup\",\n            \"_client_max_size\",\n            \"_cleanup_ctx\",\n        ]\n    )\n\n    def __init__(\n        self,\n        *,\n        logger: logging.Logger = web_logger,\n        router: Optional[UrlDispatcher] = None,\n        middlewares: Iterable[Middleware] = (),\n        handler_args: Optional[Mapping[str, Any]] = None,\n        client_max_size: int = 1024**2,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        debug: Any = ...,  # mypy doesn't support ellipsis\n    ) -> None:\n        if router is None:\n            router = UrlDispatcher()\n        else:\n            warnings.warn(\n                \"router argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        assert isinstance(router, AbstractRouter), router\n\n        if loop is not None:\n            warnings.warn(\n                \"loop argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n\n        if debug is not ...:\n            warnings.warn(\n                \"debug argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        self._debug = debug\n        self._router: UrlDispatcher = router\n        self._loop = loop\n        self._handler_args = handler_args\n        self.logger = logger\n\n        self._middlewares: _Middlewares = FrozenList(middlewares)\n\n        # initialized on freezing\n        self._middlewares_handlers: _MiddlewaresHandlers = None\n        # initialized on freezing\n        self._run_middlewares: Optional[bool] = None\n\n        self._state: Dict[Union[AppKey[Any], str], object] = {}\n        self._frozen = False\n        self._pre_frozen = False\n        self._subapps: _Subapps = []\n\n        self._on_response_prepare: _RespPrepareSignal = Signal(self)\n        self._on_startup: _AppSignal = Signal(self)\n        self._on_shutdown: _AppSignal = Signal(self)\n        self._on_cleanup: _AppSignal = Signal(self)\n        self._cleanup_ctx = CleanupContext()\n        self._on_startup.append(self._cleanup_ctx._on_startup)\n        self._on_cleanup.append(self._cleanup_ctx._on_cleanup)\n        self._client_max_size = client_max_size\n\n    def __init_subclass__(cls: Type[\"Application\"]) -> None:\n        warnings.warn(\n            \"Inheritance class {} from web.Application \"\n            \"is discouraged\".format(cls.__name__),\n            DeprecationWarning,\n            stacklevel=3,\n        )\n\n    if DEBUG:  # pragma: no cover\n\n        def __setattr__(self, name: str, val: Any) -> None:\n            if name not in self.ATTRS:\n                warnings.warn(\n                    \"Setting custom web.Application.{} attribute \"\n                    \"is discouraged\".format(name),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            super().__setattr__(name, val)\n\n    # MutableMapping API\n\n    def __eq__(self, other: object) -> bool:\n        return self is other\n\n    @overload  # type: ignore[override]\n    def __getitem__(self, key: AppKey[_T]) -> _T: ...\n\n    @overload\n    def __getitem__(self, key: str) -> Any: ...\n\n    def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:\n        return self._state[key]\n\n    def _check_frozen(self) -> None:\n        if self._frozen:\n            warnings.warn(\n                \"Changing state of started or joined \" \"application is deprecated\",\n                DeprecationWarning,\n                stacklevel=3,\n            )\n\n    @overload  # type: ignore[override]\n    def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...\n\n    @overload\n    def __setitem__(self, key: str, value: Any) -> None: ...\n\n    def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:\n        self._check_frozen()\n        if not isinstance(key, AppKey):\n            warnings.warn(\n                \"It is recommended to use web.AppKey instances for keys.\\n\"\n                + \"https://docs.aiohttp.org/en/stable/web_advanced.html\"\n                + \"#application-s-config\",\n                category=NotAppKeyWarning,\n                stacklevel=2,\n            )\n        self._state[key] = value\n\n    def __delitem__(self, key: Union[str, AppKey[_T]]) -> None:\n        self._check_frozen()\n        del self._state[key]\n\n    def __len__(self) -> int:\n        return len(self._state)\n\n    def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]:\n        return iter(self._state)\n\n    @overload  # type: ignore[override]\n    def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...\n\n    @overload\n    def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...\n\n    @overload\n    def get(self, key: str, default: Any = ...) -> Any: ...\n\n    def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:\n        return self._state.get(key, default)\n\n    ########\n    @property\n    def loop(self) -> asyncio.AbstractEventLoop:\n        # Technically the loop can be None\n        # but we mask it by explicit type cast\n        # to provide more convenient type annotation\n        warnings.warn(\"loop property is deprecated\", DeprecationWarning, stacklevel=2)\n        return cast(asyncio.AbstractEventLoop, self._loop)\n\n    def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if self._loop is not None and self._loop is not loop:\n            raise RuntimeError(\n                \"web.Application instance initialized with different loop\"\n            )\n\n        self._loop = loop\n\n        # set loop debug\n        if self._debug is ...:\n            self._debug = loop.get_debug()\n\n        # set loop to sub applications\n        for subapp in self._subapps:\n            subapp._set_loop(loop)\n\n    @property\n    def pre_frozen(self) -> bool:\n        return self._pre_frozen\n\n    def pre_freeze(self) -> None:\n        if self._pre_frozen:\n            return\n\n        self._pre_frozen = True\n        self._middlewares.freeze()\n        self._router.freeze()\n        self._on_response_prepare.freeze()\n        self._cleanup_ctx.freeze()\n        self._on_startup.freeze()\n        self._on_shutdown.freeze()\n        self._on_cleanup.freeze()\n        self._middlewares_handlers = tuple(self._prepare_middleware())\n\n        # If current app and any subapp do not have middlewares avoid run all\n        # of the code footprint that it implies, which have a middleware\n        # hardcoded per app that sets up the current_app attribute. If no\n        # middlewares are configured the handler will receive the proper\n        # current_app without needing all of this code.\n        self._run_middlewares = True if self.middlewares else False\n\n        for subapp in self._subapps:\n            subapp.pre_freeze()\n            self._run_middlewares = self._run_middlewares or subapp._run_middlewares\n\n    @property\n    def frozen(self) -> bool:\n        return self._frozen\n\n    def freeze(self) -> None:\n        if self._frozen:\n            return\n\n        self.pre_freeze()\n        self._frozen = True\n        for subapp in self._subapps:\n            subapp.freeze()\n\n    @property\n    def debug(self) -> bool:\n        warnings.warn(\"debug property is deprecated\", DeprecationWarning, stacklevel=2)\n        return self._debug  # type: ignore[no-any-return]\n\n    def _reg_subapp_signals(self, subapp: \"Application\") -> None:\n        def reg_handler(signame: str) -> None:\n            subsig = getattr(subapp, signame)\n\n            async def handler(app: \"Application\") -> None:\n                await subsig.send(subapp)\n\n            appsig = getattr(self, signame)\n            appsig.append(handler)\n\n        reg_handler(\"on_startup\")\n        reg_handler(\"on_shutdown\")\n        reg_handler(\"on_cleanup\")\n\n    def add_subapp(self, prefix: str, subapp: \"Application\") -> PrefixedSubAppResource:\n        if not isinstance(prefix, str):\n            raise TypeError(\"Prefix must be str\")\n        prefix = prefix.rstrip(\"/\")\n        if not prefix:\n            raise ValueError(\"Prefix cannot be empty\")\n        factory = partial(PrefixedSubAppResource, prefix, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def _add_subapp(\n        self, resource_factory: Callable[[], _Resource], subapp: \"Application\"\n    ) -> _Resource:\n        if self.frozen:\n            raise RuntimeError(\"Cannot add sub application to frozen application\")\n        if subapp.frozen:\n            raise RuntimeError(\"Cannot add frozen application\")\n        resource = resource_factory()\n        self.router.register_resource(resource)\n        self._reg_subapp_signals(subapp)\n        self._subapps.append(subapp)\n        subapp.pre_freeze()\n        if self._loop is not None:\n            subapp._set_loop(self._loop)\n        return resource\n\n    def add_domain(self, domain: str, subapp: \"Application\") -> MatchedSubAppResource:\n        if not isinstance(domain, str):\n            raise TypeError(\"Domain must be str\")\n        elif \"*\" in domain:\n            rule: Domain = MaskDomain(domain)\n        else:\n            rule = Domain(domain)\n        factory = partial(MatchedSubAppResource, rule, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:\n        return self.router.add_routes(routes)\n\n    @property\n    def on_response_prepare(self) -> _RespPrepareSignal:\n        return self._on_response_prepare\n\n    @property\n    def on_startup(self) -> _AppSignal:\n        return self._on_startup\n\n    @property\n    def on_shutdown(self) -> _AppSignal:\n        return self._on_shutdown\n\n    @property\n    def on_cleanup(self) -> _AppSignal:\n        return self._on_cleanup\n\n    @property\n    def cleanup_ctx(self) -> \"CleanupContext\":\n        return self._cleanup_ctx\n\n    @property\n    def router(self) -> UrlDispatcher:\n        return self._router\n\n    @property\n    def middlewares(self) -> _Middlewares:\n        return self._middlewares\n\n    def _make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        if not issubclass(access_log_class, AbstractAccessLogger):\n            raise TypeError(\n                \"access_log_class must be subclass of \"\n                \"aiohttp.abc.AbstractAccessLogger, got {}\".format(access_log_class)\n            )\n\n        self._set_loop(loop)\n        self.freeze()\n\n        kwargs[\"debug\"] = self._debug\n        kwargs[\"access_log_class\"] = access_log_class\n        if self._handler_args:\n            for k, v in self._handler_args.items():\n                kwargs[k] = v\n\n        return Server(\n            self._handle,  # type: ignore[arg-type]\n            request_factory=self._make_request,\n            loop=self._loop,\n            **kwargs,\n        )\n\n    def make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        warnings.warn(\n            \"Application.make_handler(...) is deprecated, \" \"use AppRunner API instead\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n\n        return self._make_handler(\n            loop=loop, access_log_class=access_log_class, **kwargs\n        )\n\n    async def startup(self) -> None:\n        \"\"\"Causes on_startup signal\n\n        Should be called in the event loop along with the request handler.\n        \"\"\"\n        await self.on_startup.send(self)\n\n    async def shutdown(self) -> None:\n        \"\"\"Causes on_shutdown signal\n\n        Should be called before cleanup()\n        \"\"\"\n        await self.on_shutdown.send(self)\n\n    async def cleanup(self) -> None:\n        \"\"\"Causes on_cleanup signal\n\n        Should be called after shutdown()\n        \"\"\"\n        if self.on_cleanup.frozen:\n            await self.on_cleanup.send(self)\n        else:\n            # If an exception occurs in startup, ensure cleanup contexts are completed.\n            await self._cleanup_ctx._on_cleanup(self)\n\n    def _make_request(\n        self,\n        message: RawRequestMessage,\n        payload: StreamReader,\n        protocol: RequestHandler,\n        writer: AbstractStreamWriter,\n        task: \"asyncio.Task[None]\",\n        _cls: Type[Request] = Request,\n    ) -> Request:\n        return _cls(\n            message,\n            payload,\n            protocol,\n            writer,\n            task,\n            self._loop,\n            client_max_size=self._client_max_size,\n        )\n\n    def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]:\n        for m in reversed(self._middlewares):\n            if getattr(m, \"__middleware_version__\", None) == 1:\n                yield m, True\n            else:\n                warnings.warn(\n                    'old-style middleware \"{!r}\" deprecated, ' \"see #2252\".format(m),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n                yield m, False\n\n        yield _fix_request_current_app(self), True\n\n    async def _handle(self, request: Request) -> StreamResponse:\n        loop = asyncio.get_event_loop()\n        debug = loop.get_debug()\n        match_info = await self._router.resolve(request)\n        if debug:  # pragma: no cover\n            if not isinstance(match_info, AbstractMatchInfo):\n                raise TypeError(\n                    \"match_info should be AbstractMatchInfo \"\n                    \"instance, not {!r}\".format(match_info)\n                )\n        match_info.add_app(self)\n\n        match_info.freeze()\n\n        resp = None\n        request._match_info = match_info\n        expect = request.headers.get(hdrs.EXPECT)\n        if expect:\n            resp = await match_info.expect_handler(request)\n            await request.writer.drain()\n\n        if resp is None:\n            handler = match_info.handler\n\n            if self._run_middlewares:\n                for app in match_info.apps[::-1]:\n                    for m, new_style in app._middlewares_handlers:  # type: ignore[union-attr]\n                        if new_style:\n                            handler = update_wrapper(\n                                partial(m, handler=handler), handler  # type: ignore[misc]\n                            )\n                        else:\n                            handler = await m(app, handler)  # type: ignore[arg-type,assignment]\n\n            resp = await handler(request)\n\n        return resp\n\n    def __call__(self) -> \"Application\":\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self) -> str:\n        return f\"<Application 0x{id(self):x}>\"\n\n    def __bool__(self) -> bool:\n        return True\n\n\nclass CleanupError(RuntimeError):\n    @property\n    def exceptions(self) -> List[BaseException]:\n        return cast(List[BaseException], self.args[1])\n\n\nif TYPE_CHECKING:\n    _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]]\nelse:\n    _CleanupContextBase = FrozenList\n\n\nclass CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except Exception as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)\n",
            "new_file_content": "import asyncio\nimport logging\nimport warnings\nfrom functools import partial, update_wrapper\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Awaitable,\n    Callable,\n    Dict,\n    Iterable,\n    Iterator,\n    List,\n    Mapping,\n    MutableMapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n    overload,\n)\n\nfrom aiosignal import Signal\nfrom frozenlist import FrozenList\n\nfrom . import hdrs\nfrom .abc import (\n    AbstractAccessLogger,\n    AbstractMatchInfo,\n    AbstractRouter,\n    AbstractStreamWriter,\n)\nfrom .helpers import DEBUG, AppKey\nfrom .http_parser import RawRequestMessage\nfrom .log import web_logger\nfrom .streams import StreamReader\nfrom .typedefs import Middleware\nfrom .web_exceptions import NotAppKeyWarning\nfrom .web_log import AccessLogger\nfrom .web_middlewares import _fix_request_current_app\nfrom .web_protocol import RequestHandler\nfrom .web_request import Request\nfrom .web_response import StreamResponse\nfrom .web_routedef import AbstractRouteDef\nfrom .web_server import Server\nfrom .web_urldispatcher import (\n    AbstractResource,\n    AbstractRoute,\n    Domain,\n    MaskDomain,\n    MatchedSubAppResource,\n    PrefixedSubAppResource,\n    UrlDispatcher,\n)\n\n__all__ = (\"Application\", \"CleanupError\")\n\n\nif TYPE_CHECKING:\n    _AppSignal = Signal[Callable[[\"Application\"], Awaitable[None]]]\n    _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]\n    _Middlewares = FrozenList[Middleware]\n    _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]]\n    _Subapps = List[\"Application\"]\nelse:\n    # No type checker mode, skip types\n    _AppSignal = Signal\n    _RespPrepareSignal = Signal\n    _Middlewares = FrozenList\n    _MiddlewaresHandlers = Optional[Sequence]\n    _Subapps = List\n\n_T = TypeVar(\"_T\")\n_U = TypeVar(\"_U\")\n_Resource = TypeVar(\"_Resource\", bound=AbstractResource)\n\n\nclass Application(MutableMapping[Union[str, AppKey[Any]], Any]):\n    ATTRS = frozenset(\n        [\n            \"logger\",\n            \"_debug\",\n            \"_router\",\n            \"_loop\",\n            \"_handler_args\",\n            \"_middlewares\",\n            \"_middlewares_handlers\",\n            \"_run_middlewares\",\n            \"_state\",\n            \"_frozen\",\n            \"_pre_frozen\",\n            \"_subapps\",\n            \"_on_response_prepare\",\n            \"_on_startup\",\n            \"_on_shutdown\",\n            \"_on_cleanup\",\n            \"_client_max_size\",\n            \"_cleanup_ctx\",\n        ]\n    )\n\n    def __init__(\n        self,\n        *,\n        logger: logging.Logger = web_logger,\n        router: Optional[UrlDispatcher] = None,\n        middlewares: Iterable[Middleware] = (),\n        handler_args: Optional[Mapping[str, Any]] = None,\n        client_max_size: int = 1024**2,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        debug: Any = ...,  # mypy doesn't support ellipsis\n    ) -> None:\n        if router is None:\n            router = UrlDispatcher()\n        else:\n            warnings.warn(\n                \"router argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        assert isinstance(router, AbstractRouter), router\n\n        if loop is not None:\n            warnings.warn(\n                \"loop argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n\n        if debug is not ...:\n            warnings.warn(\n                \"debug argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        self._debug = debug\n        self._router: UrlDispatcher = router\n        self._loop = loop\n        self._handler_args = handler_args\n        self.logger = logger\n\n        self._middlewares: _Middlewares = FrozenList(middlewares)\n\n        # initialized on freezing\n        self._middlewares_handlers: _MiddlewaresHandlers = None\n        # initialized on freezing\n        self._run_middlewares: Optional[bool] = None\n\n        self._state: Dict[Union[AppKey[Any], str], object] = {}\n        self._frozen = False\n        self._pre_frozen = False\n        self._subapps: _Subapps = []\n\n        self._on_response_prepare: _RespPrepareSignal = Signal(self)\n        self._on_startup: _AppSignal = Signal(self)\n        self._on_shutdown: _AppSignal = Signal(self)\n        self._on_cleanup: _AppSignal = Signal(self)\n        self._cleanup_ctx = CleanupContext()\n        self._on_startup.append(self._cleanup_ctx._on_startup)\n        self._on_cleanup.append(self._cleanup_ctx._on_cleanup)\n        self._client_max_size = client_max_size\n\n    def __init_subclass__(cls: Type[\"Application\"]) -> None:\n        warnings.warn(\n            \"Inheritance class {} from web.Application \"\n            \"is discouraged\".format(cls.__name__),\n            DeprecationWarning,\n            stacklevel=3,\n        )\n\n    if DEBUG:  # pragma: no cover\n\n        def __setattr__(self, name: str, val: Any) -> None:\n            if name not in self.ATTRS:\n                warnings.warn(\n                    \"Setting custom web.Application.{} attribute \"\n                    \"is discouraged\".format(name),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            super().__setattr__(name, val)\n\n    # MutableMapping API\n\n    def __eq__(self, other: object) -> bool:\n        return self is other\n\n    @overload  # type: ignore[override]\n    def __getitem__(self, key: AppKey[_T]) -> _T: ...\n\n    @overload\n    def __getitem__(self, key: str) -> Any: ...\n\n    def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:\n        return self._state[key]\n\n    def _check_frozen(self) -> None:\n        if self._frozen:\n            warnings.warn(\n                \"Changing state of started or joined \" \"application is deprecated\",\n                DeprecationWarning,\n                stacklevel=3,\n            )\n\n    @overload  # type: ignore[override]\n    def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...\n\n    @overload\n    def __setitem__(self, key: str, value: Any) -> None: ...\n\n    def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:\n        self._check_frozen()\n        if not isinstance(key, AppKey):\n            warnings.warn(\n                \"It is recommended to use web.AppKey instances for keys.\\n\"\n                + \"https://docs.aiohttp.org/en/stable/web_advanced.html\"\n                + \"#application-s-config\",\n                category=NotAppKeyWarning,\n                stacklevel=2,\n            )\n        self._state[key] = value\n\n    def __delitem__(self, key: Union[str, AppKey[_T]]) -> None:\n        self._check_frozen()\n        del self._state[key]\n\n    def __len__(self) -> int:\n        return len(self._state)\n\n    def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]:\n        return iter(self._state)\n\n    @overload  # type: ignore[override]\n    def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...\n\n    @overload\n    def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...\n\n    @overload\n    def get(self, key: str, default: Any = ...) -> Any: ...\n\n    def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:\n        return self._state.get(key, default)\n\n    ########\n    @property\n    def loop(self) -> asyncio.AbstractEventLoop:\n        # Technically the loop can be None\n        # but we mask it by explicit type cast\n        # to provide more convenient type annotation\n        warnings.warn(\"loop property is deprecated\", DeprecationWarning, stacklevel=2)\n        return cast(asyncio.AbstractEventLoop, self._loop)\n\n    def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if self._loop is not None and self._loop is not loop:\n            raise RuntimeError(\n                \"web.Application instance initialized with different loop\"\n            )\n\n        self._loop = loop\n\n        # set loop debug\n        if self._debug is ...:\n            self._debug = loop.get_debug()\n\n        # set loop to sub applications\n        for subapp in self._subapps:\n            subapp._set_loop(loop)\n\n    @property\n    def pre_frozen(self) -> bool:\n        return self._pre_frozen\n\n    def pre_freeze(self) -> None:\n        if self._pre_frozen:\n            return\n\n        self._pre_frozen = True\n        self._middlewares.freeze()\n        self._router.freeze()\n        self._on_response_prepare.freeze()\n        self._cleanup_ctx.freeze()\n        self._on_startup.freeze()\n        self._on_shutdown.freeze()\n        self._on_cleanup.freeze()\n        self._middlewares_handlers = tuple(self._prepare_middleware())\n\n        # If current app and any subapp do not have middlewares avoid run all\n        # of the code footprint that it implies, which have a middleware\n        # hardcoded per app that sets up the current_app attribute. If no\n        # middlewares are configured the handler will receive the proper\n        # current_app without needing all of this code.\n        self._run_middlewares = True if self.middlewares else False\n\n        for subapp in self._subapps:\n            subapp.pre_freeze()\n            self._run_middlewares = self._run_middlewares or subapp._run_middlewares\n\n    @property\n    def frozen(self) -> bool:\n        return self._frozen\n\n    def freeze(self) -> None:\n        if self._frozen:\n            return\n\n        self.pre_freeze()\n        self._frozen = True\n        for subapp in self._subapps:\n            subapp.freeze()\n\n    @property\n    def debug(self) -> bool:\n        warnings.warn(\"debug property is deprecated\", DeprecationWarning, stacklevel=2)\n        return self._debug  # type: ignore[no-any-return]\n\n    def _reg_subapp_signals(self, subapp: \"Application\") -> None:\n        def reg_handler(signame: str) -> None:\n            subsig = getattr(subapp, signame)\n\n            async def handler(app: \"Application\") -> None:\n                await subsig.send(subapp)\n\n            appsig = getattr(self, signame)\n            appsig.append(handler)\n\n        reg_handler(\"on_startup\")\n        reg_handler(\"on_shutdown\")\n        reg_handler(\"on_cleanup\")\n\n    def add_subapp(self, prefix: str, subapp: \"Application\") -> PrefixedSubAppResource:\n        if not isinstance(prefix, str):\n            raise TypeError(\"Prefix must be str\")\n        prefix = prefix.rstrip(\"/\")\n        if not prefix:\n            raise ValueError(\"Prefix cannot be empty\")\n        factory = partial(PrefixedSubAppResource, prefix, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def _add_subapp(\n        self, resource_factory: Callable[[], _Resource], subapp: \"Application\"\n    ) -> _Resource:\n        if self.frozen:\n            raise RuntimeError(\"Cannot add sub application to frozen application\")\n        if subapp.frozen:\n            raise RuntimeError(\"Cannot add frozen application\")\n        resource = resource_factory()\n        self.router.register_resource(resource)\n        self._reg_subapp_signals(subapp)\n        self._subapps.append(subapp)\n        subapp.pre_freeze()\n        if self._loop is not None:\n            subapp._set_loop(self._loop)\n        return resource\n\n    def add_domain(self, domain: str, subapp: \"Application\") -> MatchedSubAppResource:\n        if not isinstance(domain, str):\n            raise TypeError(\"Domain must be str\")\n        elif \"*\" in domain:\n            rule: Domain = MaskDomain(domain)\n        else:\n            rule = Domain(domain)\n        factory = partial(MatchedSubAppResource, rule, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:\n        return self.router.add_routes(routes)\n\n    @property\n    def on_response_prepare(self) -> _RespPrepareSignal:\n        return self._on_response_prepare\n\n    @property\n    def on_startup(self) -> _AppSignal:\n        return self._on_startup\n\n    @property\n    def on_shutdown(self) -> _AppSignal:\n        return self._on_shutdown\n\n    @property\n    def on_cleanup(self) -> _AppSignal:\n        return self._on_cleanup\n\n    @property\n    def cleanup_ctx(self) -> \"CleanupContext\":\n        return self._cleanup_ctx\n\n    @property\n    def router(self) -> UrlDispatcher:\n        return self._router\n\n    @property\n    def middlewares(self) -> _Middlewares:\n        return self._middlewares\n\n    def _make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        if not issubclass(access_log_class, AbstractAccessLogger):\n            raise TypeError(\n                \"access_log_class must be subclass of \"\n                \"aiohttp.abc.AbstractAccessLogger, got {}\".format(access_log_class)\n            )\n\n        self._set_loop(loop)\n        self.freeze()\n\n        kwargs[\"debug\"] = self._debug\n        kwargs[\"access_log_class\"] = access_log_class\n        if self._handler_args:\n            for k, v in self._handler_args.items():\n                kwargs[k] = v\n\n        return Server(\n            self._handle,  # type: ignore[arg-type]\n            request_factory=self._make_request,\n            loop=self._loop,\n            **kwargs,\n        )\n\n    def make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        warnings.warn(\n            \"Application.make_handler(...) is deprecated, \" \"use AppRunner API instead\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n\n        return self._make_handler(\n            loop=loop, access_log_class=access_log_class, **kwargs\n        )\n\n    async def startup(self) -> None:\n        \"\"\"Causes on_startup signal\n\n        Should be called in the event loop along with the request handler.\n        \"\"\"\n        await self.on_startup.send(self)\n\n    async def shutdown(self) -> None:\n        \"\"\"Causes on_shutdown signal\n\n        Should be called before cleanup()\n        \"\"\"\n        await self.on_shutdown.send(self)\n\n    async def cleanup(self) -> None:\n        \"\"\"Causes on_cleanup signal\n\n        Should be called after shutdown()\n        \"\"\"\n        if self.on_cleanup.frozen:\n            await self.on_cleanup.send(self)\n        else:\n            # If an exception occurs in startup, ensure cleanup contexts are completed.\n            await self._cleanup_ctx._on_cleanup(self)\n\n    def _make_request(\n        self,\n        message: RawRequestMessage,\n        payload: StreamReader,\n        protocol: RequestHandler,\n        writer: AbstractStreamWriter,\n        task: \"asyncio.Task[None]\",\n        _cls: Type[Request] = Request,\n    ) -> Request:\n        return _cls(\n            message,\n            payload,\n            protocol,\n            writer,\n            task,\n            self._loop,\n            client_max_size=self._client_max_size,\n        )\n\n    def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]:\n        for m in reversed(self._middlewares):\n            if getattr(m, \"__middleware_version__\", None) == 1:\n                yield m, True\n            else:\n                warnings.warn(\n                    'old-style middleware \"{!r}\" deprecated, ' \"see #2252\".format(m),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n                yield m, False\n\n        yield _fix_request_current_app(self), True\n\n    async def _handle(self, request: Request) -> StreamResponse:\n        loop = asyncio.get_event_loop()\n        debug = loop.get_debug()\n        match_info = await self._router.resolve(request)\n        if debug:  # pragma: no cover\n            if not isinstance(match_info, AbstractMatchInfo):\n                raise TypeError(\n                    \"match_info should be AbstractMatchInfo \"\n                    \"instance, not {!r}\".format(match_info)\n                )\n        match_info.add_app(self)\n\n        match_info.freeze()\n\n        resp = None\n        request._match_info = match_info\n        expect = request.headers.get(hdrs.EXPECT)\n        if expect:\n            resp = await match_info.expect_handler(request)\n            await request.writer.drain()\n\n        if resp is None:\n            handler = match_info.handler\n\n            if self._run_middlewares:\n                for app in match_info.apps[::-1]:\n                    for m, new_style in app._middlewares_handlers:  # type: ignore[union-attr]\n                        if new_style:\n                            handler = update_wrapper(\n                                partial(m, handler=handler), handler  # type: ignore[misc]\n                            )\n                        else:\n                            handler = await m(app, handler)  # type: ignore[arg-type,assignment]\n\n            resp = await handler(request)\n\n        return resp\n\n    def __call__(self) -> \"Application\":\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self) -> str:\n        return f\"<Application 0x{id(self):x}>\"\n\n    def __bool__(self) -> bool:\n        return True\n\n\nclass CleanupError(RuntimeError):\n    @property\n    def exceptions(self) -> List[BaseException]:\n        return cast(List[BaseException], self.args[1])\n\n\nif TYPE_CHECKING:\n    _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]]\nelse:\n    _CleanupContextBase = FrozenList\n\n\nclass CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except (Exception, asyncio.CancelledError) as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)\n",
            "header": {
                "file": {
                    "path": "aiohttp/web_app.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "3b4b6489",
                "new_commit_hash": "3510bffd",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/web_app.py"
            },
            "plus_file": {
                "path": "b/aiohttp/web_app.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 579,
                            "length": 7
                        },
                        "new_range": {
                            "start": 579,
                            "length": 7
                        },
                        "section": "class CleanupContext(_CleanupContextBase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                await it.__anext__()",
                                "type": "context"
                            },
                            {
                                "content": "            except StopAsyncIteration:",
                                "type": "context"
                            },
                            {
                                "content": "                pass",
                                "type": "context"
                            },
                            {
                                "content": "            except Exception as exc:",
                                "type": "deleted"
                            },
                            {
                                "content": "            except (Exception, asyncio.CancelledError) as exc:",
                                "type": "added"
                            },
                            {
                                "content": "                errors.append(exc)",
                                "type": "context"
                            },
                            {
                                "content": "            else:",
                                "type": "context"
                            },
                            {
                                "content": "                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web_app.py",
                            "type": "class",
                            "name": "CleanupContext",
                            "content": "class CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except Exception as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 564,
                            "end_lineno": 590,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/web_app.py",
                            "type": "method",
                            "name": "CleanupContext._on_cleanup",
                            "content": "class CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except Exception as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 575,
                            "end_lineno": 590,
                            "parent": {
                                "file_name": "aiohttp/web_app.py",
                                "type": "class",
                                "name": "CleanupContext",
                                "content": "class CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except Exception as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 564,
                                "end_lineno": 590,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": ".. _aiohttp-web-advanced:\n\nWeb Server Advanced\n===================\n\n.. currentmodule:: aiohttp.web\n\n\nUnicode support\n---------------\n\n*aiohttp* does :term:`requoting` of incoming request path.\n\nUnicode (non-ASCII) symbols are processed transparently on both *route\nadding* and *resolving* (internally everything is converted to\n:term:`percent-encoding` form by :term:`yarl` library).\n\nBut in case of custom regular expressions for\n:ref:`aiohttp-web-variable-handler` please take care that URL is\n*percent encoded*: if you pass Unicode patterns they don't match to\n*requoted* path.\n\n.. _aiohttp-web-peer-disconnection:\n\nPeer disconnection\n------------------\n\n*aiohttp* has 2 approaches to handling client disconnections.\nIf you are familiar with asyncio, or scalability is a concern for\nyour application, we recommend using the handler cancellation method.\n\nRaise on read/write (default)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen a client peer is gone, a subsequent reading or writing raises :exc:`OSError`\nor a more specific exception like :exc:`ConnectionResetError`.\n\nThis behavior is similar to classic WSGI frameworks like Flask and Django.\n\nThe reason for disconnection varies; it can be a network issue or explicit\nsocket closing on the peer side without reading the full server response.\n\n*aiohttp* handles disconnection properly but you can handle it explicitly, e.g.::\n\n   async def handler(request):\n       try:\n           text = await request.text()\n       except OSError:\n           # disconnected\n\n.. _web-handler-cancellation:\n\nWeb handler cancellation\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis method can be enabled using the ``handler_cancellation`` parameter\nto :func:`run_app`.\n\nWhen a client disconnects, the web handler task will be cancelled. This\nis recommended as it can reduce the load on your server when there is no\nclient to receive a response. It can also help make your application\nmore resilient to DoS attacks (by requiring an attacker to keep a\nconnection open in order to waste server resources).\n\nThis behavior is very different from classic WSGI frameworks like\nFlask and Django. It requires a reasonable level of asyncio knowledge to\nuse correctly without causing issues in your code. We provide some\nexamples here to help understand the complexity and methods\nneeded to deal with them.\n\n.. warning::\n\n   :term:`web-handler` execution could be canceled on every ``await`` or\n   ``async with`` if client drops connection without reading entire response's BODY.\n\nSometimes it is a desirable behavior: on processing ``GET`` request the\ncode might fetch data from a database or other web resource, the\nfetching is potentially slow.\n\nCanceling this fetch is a good idea: the client dropped the connection\nalready, so there is no reason to waste time and resources (memory etc)\nby getting data from a DB without any chance to send it back to the client.\n\nBut sometimes the cancellation is bad: on ``POST`` requests very often\nit is needed to save data to a DB regardless of connection closing.\n\nCancellation prevention could be implemented in several ways:\n\n* Applying :func:`aiojobs.aiohttp.shield` to a coroutine that saves data.\n* Using aiojobs_ or another third party library to run a task in the background.\n\n:func:`aiojobs.aiohttp.shield` can work well. The only disadvantage is you\nneed to split the web handler into two async functions: one for the handler\nitself and another for protected code.\n\n.. warning::\n\n   We don't recommend using :func:`asyncio.shield` for this because the shielded\n   task cannot be tracked by the application and therefore there is a risk that\n   the task will get cancelled during application shutdown. The function provided\n   by aiojobs_ operates in the same way except the inner task will be tracked\n   by the Scheduler and will get waited on during the cleanup phase.\n\nFor example the following snippet is not safe::\n\n   from aiojobs.aiohttp import shield\n\n   async def handler(request):\n       await shield(request, write_to_redis(request))\n       await shield(request, write_to_postgres(request))\n       return web.Response(text=\"OK\")\n\nCancellation might occur while saving data in REDIS, so the\n``write_to_postgres`` function will not be called, potentially\nleaving your data in an inconsistent state.\n\nInstead, you would need to write something like::\n\n   async def write_data(request):\n       await write_to_redis(request)\n       await write_to_postgres(request)\n\n   async def handler(request):\n       await shield(request, write_data(request))\n       return web.Response(text=\"OK\")\n\nAlternatively, if you want to spawn a task without waiting for\nits completion, you can use aiojobs_ which provides an API for\nspawning new background jobs. It stores all scheduled activity in\ninternal data structures and can terminate them gracefully::\n\n   from aiojobs.aiohttp import setup, spawn\n\n   async def handler(request):\n       await spawn(request, write_data())\n       return web.Response()\n\n   app = web.Application()\n   setup(app)\n   app.router.add_get(\"/\", handler)\n\n.. warning::\n\n   Don't use :func:`asyncio.create_task` for this. All tasks\n   should be awaited at some point in your code (``aiojobs`` handles\n   this for you), otherwise you will hide legitimate exceptions\n   and result in warnings being emitted.\n\n   A good case for using :func:`asyncio.create_task` is when\n   you want to run something while you are processing other data,\n   but still want to ensure the task is complete before returning::\n\n       async def handler(request):\n           t = asyncio.create_task(get_some_data())\n           ...  # Do some other things, while data is being fetched.\n           data = await t\n           return web.Response(text=data)\n\nOne more approach would be to use :func:`aiojobs.aiohttp.atomic`\ndecorator to execute the entire handler as a new job. Essentially\nrestoring the default disconnection behavior only for specific handlers::\n\n   from aiojobs.aiohttp import atomic\n\n   @atomic\n   async def handler(request):\n       await write_to_db()\n       return web.Response()\n\n   app = web.Application()\n   setup(app)\n   app.router.add_post(\"/\", handler)\n\nIt prevents all of the ``handler`` async function from cancellation,\nso ``write_to_db`` will never be interrupted.\n\n.. _aiojobs: http://aiojobs.readthedocs.io/en/latest/\n\nPassing a coroutine into run_app and Gunicorn\n---------------------------------------------\n\n:func:`run_app` accepts either application instance or a coroutine for\nmaking an application. The coroutine based approach allows to perform\nasync IO before making an app::\n\n   async def app_factory():\n       await pre_init()\n       app = web.Application()\n       app.router.add_get(...)\n       return app\n\n   web.run_app(app_factory())\n\nGunicorn worker supports a factory as well. For Gunicorn the factory\nshould accept zero parameters::\n\n   async def my_web_app():\n       app = web.Application()\n       app.router.add_get(...)\n       return app\n\nStart gunicorn:\n\n.. code-block:: shell\n\n   $ gunicorn my_app_module:my_web_app --bind localhost:8080 --worker-class aiohttp.GunicornWebWorker\n\n.. versionadded:: 3.1\n\nCustom Routing Criteria\n-----------------------\n\nSometimes you need to register :ref:`handlers <aiohttp-web-handler>` on\nmore complex criteria than simply a *HTTP method* and *path* pair.\n\nAlthough :class:`UrlDispatcher` does not support any extra criteria, routing\nbased on custom conditions can be accomplished by implementing a second layer\nof routing in your application.\n\nThe following example shows custom routing based on the *HTTP Accept* header::\n\n   class AcceptChooser:\n\n       def __init__(self):\n           self._accepts = {}\n\n       async def do_route(self, request):\n           for accept in request.headers.getall('ACCEPT', []):\n               acceptor = self._accepts.get(accept)\n               if acceptor is not None:\n                   return (await acceptor(request))\n           raise HTTPNotAcceptable()\n\n       def reg_acceptor(self, accept, handler):\n           self._accepts[accept] = handler\n\n\n   async def handle_json(request):\n       # do json handling\n\n   async def handle_xml(request):\n       # do xml handling\n\n   chooser = AcceptChooser()\n   app.add_routes([web.get('/', chooser.do_route)])\n\n   chooser.reg_acceptor('application/json', handle_json)\n   chooser.reg_acceptor('application/xml', handle_xml)\n\n.. _aiohttp-web-static-file-handling:\n\nStatic file handling\n--------------------\n\nThe best way to handle static files (images, JavaScripts, CSS files\netc.) is using `Reverse Proxy`_ like `nginx`_ or `CDN`_ services.\n\n.. _Reverse Proxy: https://en.wikipedia.org/wiki/Reverse_proxy\n.. _nginx: https://nginx.org/\n.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network\n\nBut for development it's very convenient to handle static files by\naiohttp server itself.\n\nTo do it just register a new static route by\n:meth:`RouteTableDef.static` or :func:`static` calls::\n\n   app.add_routes([web.static('/prefix', path_to_static_folder)])\n\n   routes.static('/prefix', path_to_static_folder)\n\nWhen a directory is accessed within a static route then the server responses\nto client with ``HTTP/403 Forbidden`` by default. Displaying folder index\ninstead could be enabled with ``show_index`` parameter set to ``True``::\n\n   web.static('/prefix', path_to_static_folder, show_index=True)\n\nWhen a symlink that leads outside the static directory is accessed, the server\nresponds to the client with ``HTTP/404 Not Found`` by default. To allow the server to\nfollow symlinks that lead outside the static root, the parameter ``follow_symlinks``\nshould be set to ``True``::\n\n   web.static('/prefix', path_to_static_folder, follow_symlinks=True)\n\n.. caution::\n\n   Enabling ``follow_symlinks`` can be a security risk, and may lead to\n   a directory transversal attack. You do NOT need this option to follow symlinks\n   which point to somewhere else within the static directory, this option is only\n   used to break out of the security sandbox. Enabling this option is highly\n   discouraged, and only expected to be used for edge cases in a local\n   development setting where remote users do not have access to the server.\n\nWhen you want to enable cache busting,\nparameter ``append_version`` can be set to ``True``\n\nCache busting is the process of appending some form of file version hash\nto the filename of resources like JavaScript and CSS files.\nThe performance advantage of doing this is that we can tell the browser\nto cache these files indefinitely without worrying about the client not getting\nthe latest version when the file changes::\n\n   web.static('/prefix', path_to_static_folder, append_version=True)\n\nTemplate Rendering\n------------------\n\n:mod:`aiohttp.web` does not support template rendering out-of-the-box.\n\nHowever, there is a third-party library, :mod:`aiohttp_jinja2`, which is\nsupported by the *aiohttp* authors.\n\nUsing it is rather simple. First, setup a *jinja2 environment* with a call\nto :func:`aiohttp_jinja2.setup`::\n\n    app = web.Application()\n    aiohttp_jinja2.setup(app,\n        loader=jinja2.FileSystemLoader('/path/to/templates/folder'))\n\nAfter that you may use the template engine in your\n:ref:`handlers <aiohttp-web-handler>`. The most convenient way is to simply\nwrap your handlers with the  :func:`aiohttp_jinja2.template` decorator::\n\n    @aiohttp_jinja2.template('tmpl.jinja2')\n    async def handler(request):\n        return {'name': 'Andrew', 'surname': 'Svetlov'}\n\nIf you prefer the `Mako`_ template engine, please take a look at the\n`aiohttp_mako`_ library.\n\n.. warning::\n\n   :func:`aiohttp_jinja2.template` should be applied **before**\n   :meth:`RouteTableDef.get` decorator and family, e.g. it must be\n   the *first* (most *down* decorator in the chain)::\n\n\n      @routes.get('/path')\n      @aiohttp_jinja2.template('tmpl.jinja2')\n      async def handler(request):\n          return {'name': 'Andrew', 'surname': 'Svetlov'}\n\n\n.. _Mako: http://www.makotemplates.org/\n\n.. _aiohttp_mako: https://github.com/aio-libs/aiohttp_mako\n\n\n.. _aiohttp-web-websocket-read-same-task:\n\nReading from the same task in WebSockets\n----------------------------------------\n\nReading from the *WebSocket* (``await ws.receive()``) **must only** be\ndone inside the request handler *task*; however, writing\n(``ws.send_str(...)``) to the *WebSocket*, closing (``await\nws.close()``) and canceling the handler task may be delegated to other\ntasks. See also :ref:`FAQ section\n<aiohttp_faq_terminating_websockets>`.\n\n:mod:`aiohttp.web` creates an implicit :class:`asyncio.Task` for\nhandling every incoming request.\n\n.. note::\n\n   While :mod:`aiohttp.web` itself only supports *WebSockets* without\n   downgrading to *LONG-POLLING*, etc., our team supports SockJS_, an\n   aiohttp-based library for implementing SockJS-compatible server\n   code.\n\n.. _SockJS: https://github.com/aio-libs/sockjs\n\n\n.. warning::\n\n   Parallel reads from websocket are forbidden, there is no\n   possibility to call :meth:`WebSocketResponse.receive`\n   from two tasks.\n\n   See :ref:`FAQ section <aiohttp_faq_parallel_event_sources>` for\n   instructions how to solve the problem.\n\n\n.. _aiohttp-web-data-sharing:\n\nData Sharing aka No Singletons Please\n-------------------------------------\n\n:mod:`aiohttp.web` discourages the use of *global variables*, aka *singletons*.\nEvery variable should have its own context that is *not global*.\n\nGlobal variables are generally considered bad practice due to the complexity\nthey add in keeping track of state changes to variables.\n\n*aiohttp* does not use globals by design, which will reduce the number of bugs\nand/or unexpected behaviors for its users. For example, an i18n translated string\nbeing written for one request and then being served to another.\n\nSo, :class:`Application` and :class:`Request`\nsupport a :class:`collections.abc.MutableMapping` interface (i.e. they are\ndict-like objects), allowing them to be used as data stores.\n\n\n.. _aiohttp-web-data-sharing-app-config:\n\nApplication's config\n^^^^^^^^^^^^^^^^^^^^\n\nFor storing *global-like* variables, feel free to save them in an\n:class:`Application` instance::\n\n    app['my_private_key'] = data\n\nand get it back in the :term:`web-handler`::\n\n    async def handler(request):\n        data = request.app['my_private_key']\n\nRather than using :class:`str` keys, we recommend using :class:`AppKey`.\nThis is required for type safety (e.g. when checking with mypy)::\n\n    my_private_key = web.AppKey(\"my_private_key\", str)\n    app[my_private_key] = data\n\n    async def handler(request: web.Request):\n        data = request.app[my_private_key]\n        # reveal_type(data) -> str\n\nIn case of :ref:`nested applications\n<aiohttp-web-nested-applications>` the desired lookup strategy could\nbe the following:\n\n1. Search the key in the current nested application.\n2. If the key is not found continue searching in the parent application(s).\n\nFor this please use :attr:`Request.config_dict` read-only property::\n\n    async def handler(request):\n        data = request.config_dict[my_private_key]\n\nThe app object can be used in this way to reuse a database connection or anything\nelse needed throughout the application.\n\nSee this reference section for more detail: :ref:`aiohttp-web-app-and-router`.\n\nRequest's storage\n^^^^^^^^^^^^^^^^^\n\nVariables that are only needed for the lifetime of a :class:`Request`, can be\nstored in a :class:`Request`::\n\n    async def handler(request):\n      request['my_private_key'] = \"data\"\n      ...\n\nThis is mostly useful for :ref:`aiohttp-web-middlewares` and\n:ref:`aiohttp-web-signals` handlers to store data for further processing by the\nnext handlers in the chain.\n\nResponse's storage\n^^^^^^^^^^^^^^^^^^\n\n:class:`StreamResponse` and :class:`Response` objects\nalso support :class:`collections.abc.MutableMapping` interface. This is useful\nwhen you want to share data with signals and middlewares once all the work in\nthe handler is done::\n\n    async def handler(request):\n      [ do all the work ]\n      response['my_metric'] = 123\n      return response\n\n\nNaming hint\n^^^^^^^^^^^\n\nTo avoid clashing with other *aiohttp* users and third-party libraries, please\nchoose a unique key name for storing data.\n\nIf your code is published on PyPI, then the project name is most likely unique\nand safe to use as the key.\nOtherwise, something based on your company name/url would be satisfactory (i.e.\n``org.company.app``).\n\n\n.. _aiohttp-web-contextvars:\n\n\nContextVars support\n-------------------\n\nAsyncio has :mod:`Context Variables <contextvars>` as a context-local storage\n(a generalization of thread-local concept that works with asyncio tasks also).\n\n\n*aiohttp* server supports it in the following way:\n\n* A server inherits the current task's context used when creating it.\n  :func:`aiohttp.web.run_app()` runs a task for handling all underlying jobs running\n  the app, but alternatively :ref:`aiohttp-web-app-runners` can be used.\n\n* Application initialization / finalization events (:attr:`Application.cleanup_ctx`,\n  :attr:`Application.on_startup` and :attr:`Application.on_shutdown`,\n  :attr:`Application.on_cleanup`) are executed inside the same context.\n\n  E.g. all context modifications made on application startup are visible on teardown.\n\n* On every request handling *aiohttp* creates a context copy. :term:`web-handler` has\n  all variables installed on initialization stage. But the context modification made by\n  a handler or middleware is invisible to another HTTP request handling call.\n\nAn example of context vars usage::\n\n    from contextvars import ContextVar\n\n    from aiohttp import web\n\n    VAR = ContextVar('VAR', default='default')\n\n\n    async def coro():\n        return VAR.get()\n\n\n    async def handler(request):\n        var = VAR.get()\n        VAR.set('handler')\n        ret = await coro()\n        return web.Response(text='\\n'.join([var,\n                                            ret]))\n\n\n    async def on_startup(app):\n        print('on_startup', VAR.get())\n        VAR.set('on_startup')\n\n\n    async def on_cleanup(app):\n        print('on_cleanup', VAR.get())\n        VAR.set('on_cleanup')\n\n\n    async def init():\n        print('init', VAR.get())\n        VAR.set('init')\n        app = web.Application()\n        app.router.add_get('/', handler)\n\n        app.on_startup.append(on_startup)\n        app.on_cleanup.append(on_cleanup)\n        return app\n\n\n    web.run_app(init())\n    print('done', VAR.get())\n\n.. versionadded:: 3.5\n\n\n.. _aiohttp-web-middlewares:\n\nMiddlewares\n-----------\n\n:mod:`aiohttp.web` provides a powerful mechanism for customizing\n:ref:`request handlers<aiohttp-web-handler>` via *middlewares*.\n\nA *middleware* is a coroutine that can modify either the request or\nresponse. For example, here's a simple *middleware* which appends\n``' wink'`` to the response::\n\n    from aiohttp.web import middleware\n\n    @middleware\n    async def middleware(request, handler):\n        resp = await handler(request)\n        resp.text = resp.text + ' wink'\n        return resp\n\n.. note::\n\n   The example won't work with streamed responses or websockets\n\nEvery *middleware* should accept two parameters, a :class:`request\n<Request>` instance and a *handler*, and return the response or raise\nan exception. If the exception is not an instance of\n:exc:`HTTPException` it is converted to ``500``\n:exc:`HTTPInternalServerError` after processing the\nmiddlewares chain.\n\n.. warning::\n\n   Second argument should be named *handler* exactly.\n\nWhen creating an :class:`Application`, these *middlewares* are passed to\nthe keyword-only ``middlewares`` parameter::\n\n   app = web.Application(middlewares=[middleware_1,\n                                      middleware_2])\n\nInternally, a single :ref:`request handler <aiohttp-web-handler>` is constructed\nby applying the middleware chain to the original handler in reverse order,\nand is called by the :class:`~aiohttp.web.RequestHandler` as a regular *handler*.\n\nSince *middlewares* are themselves coroutines, they may perform extra\n``await`` calls when creating a new handler, e.g. call database etc.\n\n*Middlewares* usually call the handler, but they may choose to ignore it,\ne.g. displaying *403 Forbidden page* or raising :exc:`HTTPForbidden` exception\nif the user does not have permissions to access the underlying resource.\nThey may also render errors raised by the handler, perform some pre- or\npost-processing like handling *CORS* and so on.\n\nThe following code demonstrates middlewares execution order::\n\n   from aiohttp import web\n\n   async def test(request):\n       print('Handler function called')\n       return web.Response(text=\"Hello\")\n\n   @web.middleware\n   async def middleware1(request, handler):\n       print('Middleware 1 called')\n       response = await handler(request)\n       print('Middleware 1 finished')\n       return response\n\n   @web.middleware\n   async def middleware2(request, handler):\n       print('Middleware 2 called')\n       response = await handler(request)\n       print('Middleware 2 finished')\n       return response\n\n\n   app = web.Application(middlewares=[middleware1, middleware2])\n   app.router.add_get('/', test)\n   web.run_app(app)\n\nProduced output::\n\n   Middleware 1 called\n   Middleware 2 called\n   Handler function called\n   Middleware 2 finished\n   Middleware 1 finished\n\nExample\n^^^^^^^\n\nA common use of middlewares is to implement custom error pages.  The following\nexample will render 404 errors using a JSON response, as might be appropriate\na JSON REST service::\n\n    from aiohttp import web\n\n    @web.middleware\n    async def error_middleware(request, handler):\n        try:\n            response = await handler(request)\n            if response.status != 404:\n                return response\n            message = response.message\n        except web.HTTPException as ex:\n            if ex.status != 404:\n                raise\n            message = ex.reason\n        return web.json_response({'error': message})\n\n    app = web.Application(middlewares=[error_middleware])\n\n\nMiddleware Factory\n^^^^^^^^^^^^^^^^^^\n\nA *middleware factory* is a function that creates a middleware with passed arguments. For example, here's a trivial *middleware factory*::\n\n    def middleware_factory(text):\n        @middleware\n        async def sample_middleware(request, handler):\n            resp = await handler(request)\n            resp.text = resp.text + text\n            return resp\n        return sample_middleware\n\nRemember that contrary to regular middlewares you need the result of a middleware factory not the function itself. So when passing a middleware factory to an app you actually need to call it::\n\n    app = web.Application(middlewares=[middleware_factory(' wink')])\n\n.. _aiohttp-web-signals:\n\nSignals\n-------\n\nAlthough :ref:`middlewares <aiohttp-web-middlewares>` can customize\n:ref:`request handlers<aiohttp-web-handler>` before or after a :class:`Response`\nhas been prepared, they can't customize a :class:`Response` **while** it's\nbeing prepared. For this :mod:`aiohttp.web` provides *signals*.\n\nFor example, a middleware can only change HTTP headers for *unprepared*\nresponses (see :meth:`StreamResponse.prepare`), but sometimes we\nneed a hook for changing HTTP headers for streamed responses and WebSockets.\nThis can be accomplished by subscribing to the\n:attr:`Application.on_response_prepare` signal, which is called after default\nheaders have been computed and directly before headers are sent::\n\n    async def on_prepare(request, response):\n        response.headers['My-Header'] = 'value'\n\n    app.on_response_prepare.append(on_prepare)\n\n\nAdditionally, the :attr:`Application.on_startup` and\n:attr:`Application.on_cleanup` signals can be subscribed to for\napplication component setup and tear down accordingly.\n\nThe following example will properly initialize and dispose an asyncpg connection\nengine::\n\n    from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine\n\n    pg_engine = web.AppKey(\"pg_engine\", AsyncEngine)\n\n    async def create_pg(app):\n        app[pg_engine] = await create_async_engine(\n            \"postgresql+asyncpg://postgre:@localhost:5432/postgre\"\n        )\n\n    async def dispose_pg(app):\n        await app[pg_engine].dispose()\n\n    app.on_startup.append(create_pg)\n    app.on_cleanup.append(dispose_pg)\n\n\nSignal handlers should not return a value but may modify incoming mutable\nparameters.\n\nSignal handlers will be run sequentially, in order they were\nadded. All handlers must be asynchronous since *aiohttp* 3.0.\n\n.. _aiohttp-web-cleanup-ctx:\n\nCleanup Context\n---------------\n\nBare :attr:`Application.on_startup` / :attr:`Application.on_cleanup`\npair still has a pitfall: signals handlers are independent on each other.\n\nE.g. we have ``[create_pg, create_redis]`` in *startup* signal and\n``[dispose_pg, dispose_redis]`` in *cleanup*.\n\nIf, for example, ``create_pg(app)`` call fails ``create_redis(app)``\nis not called. But on application cleanup both ``dispose_pg(app)`` and\n``dispose_redis(app)`` are still called: *cleanup signal* has no\nknowledge about startup/cleanup pairs and their execution state.\n\n\nThe solution is :attr:`Application.cleanup_ctx` usage::\n\n    async def pg_engine(app: web.Application):\n        app[pg_engine] = await create_async_engine(\n            \"postgresql+asyncpg://postgre:@localhost:5432/postgre\"\n        )\n        yield\n        await app[pg_engine].dispose()\n\n    app.cleanup_ctx.append(pg_engine)\n\nThe attribute is a list of *asynchronous generators*, a code *before*\n``yield`` is an initialization stage (called on *startup*), a code\n*after* ``yield`` is executed on *cleanup*. The generator must have only\none ``yield``.\n\n*aiohttp* guarantees that *cleanup code* is called if and only if\n*startup code* was successfully finished.\n\n.. versionadded:: 3.1\n\n.. _aiohttp-web-nested-applications:\n\nNested applications\n-------------------\n\nSub applications are designed for solving the problem of the big\nmonolithic code base.\nLet's assume we have a project with own business logic and tools like\nadministration panel and debug toolbar.\n\nAdministration panel is a separate application by its own nature but all\ntoolbar URLs are served by prefix like ``/admin``.\n\nThus we'll create a totally separate application named ``admin`` and\nconnect it to main app with prefix by\n:meth:`Application.add_subapp`::\n\n   admin = web.Application()\n   # setup admin routes, signals and middlewares\n\n   app.add_subapp('/admin/', admin)\n\nMiddlewares and signals from ``app`` and ``admin`` are chained.\n\nIt means that if URL is ``'/admin/something'`` middlewares from\n``app`` are applied first and ``admin.middlewares`` are the next in\nthe call chain.\n\nThe same is going for\n:attr:`Application.on_response_prepare` signal -- the\nsignal is delivered to both top level ``app`` and ``admin`` if\nprocessing URL is routed to ``admin`` sub-application.\n\nCommon signals like :attr:`Application.on_startup`,\n:attr:`Application.on_shutdown` and\n:attr:`Application.on_cleanup` are delivered to all\nregistered sub-applications. The passed parameter is sub-application\ninstance, not top-level application.\n\n\nThird level sub-applications can be nested into second level ones --\nthere are no limitation for nesting level.\n\nUrl reversing for sub-applications should generate urls with proper prefix.\n\nBut for getting URL sub-application's router should be used::\n\n   admin = web.Application()\n   admin.add_routes([web.get('/resource', handler, name='name')])\n\n   app.add_subapp('/admin/', admin)\n\n   url = admin.router['name'].url_for()\n\nThe generated ``url`` from example will have a value\n``URL('/admin/resource')``.\n\nIf main application should do URL reversing for sub-application it could\nuse the following explicit technique::\n\n   admin = web.Application()\n   admin_key = web.AppKey('admin_key', web.Application)\n   admin.add_routes([web.get('/resource', handler, name='name')])\n\n   app.add_subapp('/admin/', admin)\n   app[admin_key] = admin\n\n   async def handler(request: web.Request):  # main application's handler\n       admin = request.app[admin_key]\n       url = admin.router['name'].url_for()\n\n.. _aiohttp-web-expect-header:\n\n*Expect* Header\n---------------\n\n:mod:`aiohttp.web` supports *Expect* header. By default it sends\n``HTTP/1.1 100 Continue`` line to client, or raises\n:exc:`HTTPExpectationFailed` if header value is not equal to\n\"100-continue\". It is possible to specify custom *Expect* header\nhandler on per route basis. This handler gets called if *Expect*\nheader exist in request after receiving all headers and before\nprocessing application's :ref:`aiohttp-web-middlewares` and\nroute handler. Handler can return *None*, in that case the request\nprocessing continues as usual. If handler returns an instance of class\n:class:`StreamResponse`, *request handler* uses it as response. Also\nhandler can raise a subclass of :exc:`HTTPException`. In this case all\nfurther processing will not happen and client will receive appropriate\nhttp response.\n\n.. note::\n    A server that does not understand or is unable to comply with any of the\n    expectation values in the Expect field of a request MUST respond with\n    appropriate error status. The server MUST respond with a 417\n    (Expectation Failed) status if any of the expectations cannot be met or,\n    if there are other problems with the request, some other 4xx status.\n\n    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20\n\nIf all checks pass, the custom handler *must* write a *HTTP/1.1 100 Continue*\nstatus code before returning.\n\nThe following example shows how to setup a custom handler for the *Expect*\nheader::\n\n   async def check_auth(request):\n       if request.version != aiohttp.HttpVersion11:\n           return\n\n       if request.headers.get('EXPECT') != '100-continue':\n           raise HTTPExpectationFailed(text=\"Unknown Expect: %s\" % expect)\n\n       if request.headers.get('AUTHORIZATION') is None:\n           raise HTTPForbidden()\n\n       request.transport.write(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\")\n\n   async def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\n   app = web.Application()\n   app.add_routes([web.add_get('/', hello, expect_handler=check_auth)])\n\n.. _aiohttp-web-custom-resource:\n\nCustom resource implementation\n------------------------------\n\nTo register custom resource use :meth:`~aiohttp.web.UrlDispatcher.register_resource`.\nResource instance must implement `AbstractResource` interface.\n\n.. _aiohttp-web-app-runners:\n\nApplication runners\n-------------------\n\n:func:`run_app` provides a simple *blocking* API for running an\n:class:`Application`.\n\nFor starting the application *asynchronously* or serving on multiple\nHOST/PORT :class:`AppRunner` exists.\n\nThe simple startup code for serving HTTP site on ``'localhost'``, port\n``8080`` looks like::\n\n    runner = web.AppRunner(app)\n    await runner.setup()\n    site = web.TCPSite(runner, 'localhost', 8080)\n    await site.start()\n\n    while True:\n        await asyncio.sleep(3600)  # sleep forever\n\nTo stop serving call :meth:`AppRunner.cleanup`::\n\n    await runner.cleanup()\n\n.. versionadded:: 3.0\n\n.. _aiohttp-web-graceful-shutdown:\n\nGraceful shutdown\n------------------\n\nStopping *aiohttp web server* by just closing all connections is not\nalways satisfactory.\n\nWhen aiohttp is run with :func:`run_app`, it will attempt a graceful shutdown\nby following these steps (if using a :ref:`runner <aiohttp-web-app-runners>`,\nthen calling :meth:`AppRunner.cleanup` will perform these steps, excluding\nstep 7).\n\n1. Stop each site listening on sockets, so new connections will be rejected.\n2. Close idle keep-alive connections (and set active ones to close upon completion).\n3. Call the :attr:`Application.on_shutdown` signal. This should be used to shutdown\n   long-lived connections, such as websockets (see below).\n4. Wait a short time for running handlers to complete. This allows any pending handlers\n   to complete successfully. The timeout can be adjusted with ``shutdown_timeout``\n   in :func:`run_app`.\n5. Close any remaining connections and cancel their handlers. It will wait on the\n   canceling handlers for a short time, again adjustable with ``shutdown_timeout``.\n6. Call the :attr:`Application.on_cleanup` signal. This should be used to cleanup any\n   resources (such as DB connections). This includes completing the\n   :ref:`cleanup contexts<aiohttp-web-cleanup-ctx>` which may be used to ensure\n   background tasks are completed successfully (see\n   :ref:`handler cancellation<web-handler-cancellation>` or aiojobs_ for examples).\n7. Cancel any remaining tasks and wait on them to complete.\n\nWebsocket shutdown\n^^^^^^^^^^^^^^^^^^\n\nOne problem is if the application supports :term:`websockets <websocket>` or\n*data streaming* it most likely has open connections at server shutdown time.\n\nThe *library* has no knowledge how to close them gracefully but a developer can\nhelp by registering an :attr:`Application.on_shutdown` signal handler.\n\nA developer should keep a list of opened connections\n(:class:`Application` is a good candidate).\n\nThe following :term:`websocket` snippet shows an example of a websocket handler::\n\n    from aiohttp import web\n    import weakref\n\n    app = web.Application()\n    websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n    app[websockets] = weakref.WeakSet()\n\n    async def websocket_handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        request.app[websockets].add(ws)\n        try:\n            async for msg in ws:\n                ...\n        finally:\n            request.app[websockets].discard(ws)\n\n        return ws\n\nThen the signal handler may look like::\n\n    from aiohttp import WSCloseCode\n\n    async def on_shutdown(app):\n        for ws in set(app[websockets]):\n            await ws.close(code=WSCloseCode.GOING_AWAY, message=\"Server shutdown\")\n\n    app.on_shutdown.append(on_shutdown)\n\n.. _aiohttp-web-ceil-absolute-timeout:\n\nCeil of absolute timeout value\n------------------------------\n\n*aiohttp* **ceils** internal timeout values if the value is equal or\ngreater than 5 seconds. The timeout expires at the next integer second\ngreater than ``current_time + timeout``.\n\nMore details about ceiling absolute timeout values is available here\n:ref:`aiohttp-client-timeouts`.\n\nThe default threshold can be configured at :class:`aiohttp.web.Application`\nlevel using the ``handler_args`` parameter.\n\n.. code-block:: python3\n\n    app = web.Application(handler_args={\"timeout_ceil_threshold\": 1})\n\n.. _aiohttp-web-background-tasks:\n\nBackground tasks\n-----------------\n\nSometimes there's a need to perform some asynchronous operations just\nafter application start-up.\n\nEven more, in some sophisticated systems there could be a need to run some\nbackground tasks in the event loop along with the application's request\nhandler. Such as listening to message queue or other network message/event\nsources (e.g. ZeroMQ, Redis Pub/Sub, AMQP, etc.) to react to received messages\nwithin the application.\n\nFor example the background task could listen to ZeroMQ on\n``zmq.SUB`` socket, process and forward retrieved messages to\nclients connected via WebSocket that are stored somewhere in the\napplication (e.g. in the ``application['websockets']`` list).\n\nTo run such short and long running background tasks aiohttp provides an\nability to register :attr:`Application.on_startup` signal handler(s) that\nwill run along with the application's request handler.\n\nFor example there's a need to run one quick task and two long running\ntasks that will live till the application is alive. The appropriate\nbackground tasks could be registered as an :attr:`Application.on_startup`\nsignal handler or :attr:`Application.cleanup_ctx` as shown in the example\nbelow::\n\n  async def listen_to_redis(app: web.Application):\n      client = redis.from_url(\"redis://localhost:6379\")\n      channel = \"news\"\n      async with client.pubsub() as pubsub:\n          await pubsub.subscribe(channel)\n          while True:\n              try:\n                  msg = await pubsub.get_message(ignore_subscribe_messages=True)\n                  if msg is not None:\n                      for ws in app[\"websockets\"]:\n                          await ws.send_str(\"{}: {}\".format(channel, msg))\n              except asyncio.CancelledError:\n                  break\n\n\n  async def background_tasks(app):\n      app[redis_listener] = asyncio.create_task(listen_to_redis(app))\n\n      yield\n\n      app[redis_listener].cancel()\n      await app[redis_listener]\n\n\n  app = web.Application()\n  redis_listener = web.AppKey(\"redis_listener\", asyncio.Task[None])\n  app.cleanup_ctx.append(background_tasks)\n  web.run_app(app)\n\n\nThe task ``listen_to_redis`` will run forever.\nTo shut it down correctly :attr:`Application.on_cleanup` signal handler\nmay be used to send a cancellation to it.\n\n.. _aiohttp-web-complex-applications:\n\nComplex Applications\n^^^^^^^^^^^^^^^^^^^^\n\nSometimes aiohttp is not the sole part of an application and additional\ntasks/processes may need to be run alongside the aiohttp :class:`Application`.\n\nGenerally, the best way to achieve this is to use :func:`aiohttp.web.run_app`\nas the entry point for the program. Other tasks can then be run via\n:attr:`Application.startup` and :attr:`Application.on_cleanup`. By having the\n:class:`Application` control the lifecycle of the entire program, the code\nwill be more robust and ensure that the tasks are started and stopped along\nwith the application.\n\nFor example, running a long-lived task alongside the :class:`Application`\ncan be done with a :ref:`aiohttp-web-cleanup-ctx` function like::\n\n\n  async def run_other_task(_app):\n      task = asyncio.create_task(other_long_task())\n\n      yield\n\n      task.cancel()\n      with suppress(asyncio.CancelledError):\n          await task  # Ensure any exceptions etc. are raised.\n\n  app.cleanup_ctx.append(run_other_task)\n\n\nOr a separate process can be run with something like::\n\n\n  async def run_process(_app):\n      proc = await asyncio.create_subprocess_exec(path)\n\n      yield\n\n      if proc.returncode is None:\n          proc.terminate()\n      await proc.wait()\n\n  app.cleanup_ctx.append(run_process)\n\n\nHandling error pages\n--------------------\n\nPages like *404 Not Found* and *500 Internal Error* could be handled\nby custom middleware, see :ref:`polls demo <aiohttpdemos:aiohttp-demos-polls-middlewares>`\nfor example.\n\n.. _aiohttp-web-forwarded-support:\n\nDeploying behind a Proxy\n------------------------\n\nAs discussed in :ref:`aiohttp-deployment` the preferable way is\ndeploying *aiohttp* web server behind a *Reverse Proxy Server* like\n:term:`nginx` for production usage.\n\nIn this way properties like :attr:`BaseRequest.scheme`\n:attr:`BaseRequest.host` and :attr:`BaseRequest.remote` are\nincorrect.\n\nReal values should be given from proxy server, usually either\n``Forwarded`` or old-fashion ``X-Forwarded-For``,\n``X-Forwarded-Host``, ``X-Forwarded-Proto`` HTTP headers are used.\n\n*aiohttp* does not take *forwarded* headers into account by default\nbecause it produces *security issue*: HTTP client might add these\nheaders too, pushing non-trusted data values.\n\nThat's why *aiohttp server* should setup *forwarded* headers in custom\nmiddleware in tight conjunction with *reverse proxy configuration*.\n\nFor changing :attr:`BaseRequest.scheme` :attr:`BaseRequest.host`\n:attr:`BaseRequest.remote` and :attr:`BaseRequest.client_max_size`\nthe middleware might use :meth:`BaseRequest.clone`.\n\n.. seealso::\n\n   https://github.com/aio-libs/aiohttp-remotes provides secure helpers\n   for modifying *scheme*, *host* and *remote* attributes according\n   to ``Forwarded`` and ``X-Forwarded-*`` HTTP headers.\n\nSwagger support\n---------------\n\n`aiohttp-swagger <https://github.com/cr0hn/aiohttp-swagger>`_ is a\nlibrary that allow to add Swagger documentation and embed the\nSwagger-UI into your :mod:`aiohttp.web` project.\n\nCORS support\n------------\n\n:mod:`aiohttp.web` itself does not support `Cross-Origin Resource\nSharing <https://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`_, but\nthere is an aiohttp plugin for it:\n`aiohttp_cors <https://github.com/aio-libs/aiohttp_cors>`_.\n\n\nDebug Toolbar\n-------------\n\n`aiohttp-debugtoolbar`_ is a very useful library that provides a\ndebugging toolbar while you're developing an :mod:`aiohttp.web`\napplication.\n\nInstall it with ``pip``:\n\n.. code-block:: shell\n\n    $ pip install aiohttp_debugtoolbar\n\n\nJust call :func:`aiohttp_debugtoolbar.setup`::\n\n    import aiohttp_debugtoolbar\n    from aiohttp_debugtoolbar import toolbar_middleware_factory\n\n    app = web.Application()\n    aiohttp_debugtoolbar.setup(app)\n\nThe toolbar is ready to use. Enjoy!!!\n\n.. _aiohttp-debugtoolbar: https://github.com/aio-libs/aiohttp_debugtoolbar\n\n\nDev Tools\n---------\n\n`aiohttp-devtools`_ provides a couple of tools to simplify development of\n:mod:`aiohttp.web` applications.\n\n\nInstall with ``pip``:\n\n.. code-block:: shell\n\n    $ pip install aiohttp-devtools\n\n* ``runserver`` provides a development server with auto-reload,\n  live-reload, static file serving.\n* ``start`` is a `cookiecutter command which does the donkey work\n  of creating new :mod:`aiohttp.web` Applications.\n\nDocumentation and a complete tutorial of creating and running an app\nlocally are available at `aiohttp-devtools`_.\n\n.. _aiohttp-devtools: https://github.com/aio-libs/aiohttp-devtools\n",
            "new_file_content": ".. _aiohttp-web-advanced:\n\nWeb Server Advanced\n===================\n\n.. currentmodule:: aiohttp.web\n\n\nUnicode support\n---------------\n\n*aiohttp* does :term:`requoting` of incoming request path.\n\nUnicode (non-ASCII) symbols are processed transparently on both *route\nadding* and *resolving* (internally everything is converted to\n:term:`percent-encoding` form by :term:`yarl` library).\n\nBut in case of custom regular expressions for\n:ref:`aiohttp-web-variable-handler` please take care that URL is\n*percent encoded*: if you pass Unicode patterns they don't match to\n*requoted* path.\n\n.. _aiohttp-web-peer-disconnection:\n\nPeer disconnection\n------------------\n\n*aiohttp* has 2 approaches to handling client disconnections.\nIf you are familiar with asyncio, or scalability is a concern for\nyour application, we recommend using the handler cancellation method.\n\nRaise on read/write (default)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen a client peer is gone, a subsequent reading or writing raises :exc:`OSError`\nor a more specific exception like :exc:`ConnectionResetError`.\n\nThis behavior is similar to classic WSGI frameworks like Flask and Django.\n\nThe reason for disconnection varies; it can be a network issue or explicit\nsocket closing on the peer side without reading the full server response.\n\n*aiohttp* handles disconnection properly but you can handle it explicitly, e.g.::\n\n   async def handler(request):\n       try:\n           text = await request.text()\n       except OSError:\n           # disconnected\n\n.. _web-handler-cancellation:\n\nWeb handler cancellation\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThis method can be enabled using the ``handler_cancellation`` parameter\nto :func:`run_app`.\n\nWhen a client disconnects, the web handler task will be cancelled. This\nis recommended as it can reduce the load on your server when there is no\nclient to receive a response. It can also help make your application\nmore resilient to DoS attacks (by requiring an attacker to keep a\nconnection open in order to waste server resources).\n\nThis behavior is very different from classic WSGI frameworks like\nFlask and Django. It requires a reasonable level of asyncio knowledge to\nuse correctly without causing issues in your code. We provide some\nexamples here to help understand the complexity and methods\nneeded to deal with them.\n\n.. warning::\n\n   :term:`web-handler` execution could be canceled on every ``await`` or\n   ``async with`` if client drops connection without reading entire response's BODY.\n\nSometimes it is a desirable behavior: on processing ``GET`` request the\ncode might fetch data from a database or other web resource, the\nfetching is potentially slow.\n\nCanceling this fetch is a good idea: the client dropped the connection\nalready, so there is no reason to waste time and resources (memory etc)\nby getting data from a DB without any chance to send it back to the client.\n\nBut sometimes the cancellation is bad: on ``POST`` requests very often\nit is needed to save data to a DB regardless of connection closing.\n\nCancellation prevention could be implemented in several ways:\n\n* Applying :func:`aiojobs.aiohttp.shield` to a coroutine that saves data.\n* Using aiojobs_ or another third party library to run a task in the background.\n\n:func:`aiojobs.aiohttp.shield` can work well. The only disadvantage is you\nneed to split the web handler into two async functions: one for the handler\nitself and another for protected code.\n\n.. warning::\n\n   We don't recommend using :func:`asyncio.shield` for this because the shielded\n   task cannot be tracked by the application and therefore there is a risk that\n   the task will get cancelled during application shutdown. The function provided\n   by aiojobs_ operates in the same way except the inner task will be tracked\n   by the Scheduler and will get waited on during the cleanup phase.\n\nFor example the following snippet is not safe::\n\n   from aiojobs.aiohttp import shield\n\n   async def handler(request):\n       await shield(request, write_to_redis(request))\n       await shield(request, write_to_postgres(request))\n       return web.Response(text=\"OK\")\n\nCancellation might occur while saving data in REDIS, so the\n``write_to_postgres`` function will not be called, potentially\nleaving your data in an inconsistent state.\n\nInstead, you would need to write something like::\n\n   async def write_data(request):\n       await write_to_redis(request)\n       await write_to_postgres(request)\n\n   async def handler(request):\n       await shield(request, write_data(request))\n       return web.Response(text=\"OK\")\n\nAlternatively, if you want to spawn a task without waiting for\nits completion, you can use aiojobs_ which provides an API for\nspawning new background jobs. It stores all scheduled activity in\ninternal data structures and can terminate them gracefully::\n\n   from aiojobs.aiohttp import setup, spawn\n\n   async def handler(request):\n       await spawn(request, write_data())\n       return web.Response()\n\n   app = web.Application()\n   setup(app)\n   app.router.add_get(\"/\", handler)\n\n.. warning::\n\n   Don't use :func:`asyncio.create_task` for this. All tasks\n   should be awaited at some point in your code (``aiojobs`` handles\n   this for you), otherwise you will hide legitimate exceptions\n   and result in warnings being emitted.\n\n   A good case for using :func:`asyncio.create_task` is when\n   you want to run something while you are processing other data,\n   but still want to ensure the task is complete before returning::\n\n       async def handler(request):\n           t = asyncio.create_task(get_some_data())\n           ...  # Do some other things, while data is being fetched.\n           data = await t\n           return web.Response(text=data)\n\nOne more approach would be to use :func:`aiojobs.aiohttp.atomic`\ndecorator to execute the entire handler as a new job. Essentially\nrestoring the default disconnection behavior only for specific handlers::\n\n   from aiojobs.aiohttp import atomic\n\n   @atomic\n   async def handler(request):\n       await write_to_db()\n       return web.Response()\n\n   app = web.Application()\n   setup(app)\n   app.router.add_post(\"/\", handler)\n\nIt prevents all of the ``handler`` async function from cancellation,\nso ``write_to_db`` will never be interrupted.\n\n.. _aiojobs: http://aiojobs.readthedocs.io/en/latest/\n\nPassing a coroutine into run_app and Gunicorn\n---------------------------------------------\n\n:func:`run_app` accepts either application instance or a coroutine for\nmaking an application. The coroutine based approach allows to perform\nasync IO before making an app::\n\n   async def app_factory():\n       await pre_init()\n       app = web.Application()\n       app.router.add_get(...)\n       return app\n\n   web.run_app(app_factory())\n\nGunicorn worker supports a factory as well. For Gunicorn the factory\nshould accept zero parameters::\n\n   async def my_web_app():\n       app = web.Application()\n       app.router.add_get(...)\n       return app\n\nStart gunicorn:\n\n.. code-block:: shell\n\n   $ gunicorn my_app_module:my_web_app --bind localhost:8080 --worker-class aiohttp.GunicornWebWorker\n\n.. versionadded:: 3.1\n\nCustom Routing Criteria\n-----------------------\n\nSometimes you need to register :ref:`handlers <aiohttp-web-handler>` on\nmore complex criteria than simply a *HTTP method* and *path* pair.\n\nAlthough :class:`UrlDispatcher` does not support any extra criteria, routing\nbased on custom conditions can be accomplished by implementing a second layer\nof routing in your application.\n\nThe following example shows custom routing based on the *HTTP Accept* header::\n\n   class AcceptChooser:\n\n       def __init__(self):\n           self._accepts = {}\n\n       async def do_route(self, request):\n           for accept in request.headers.getall('ACCEPT', []):\n               acceptor = self._accepts.get(accept)\n               if acceptor is not None:\n                   return (await acceptor(request))\n           raise HTTPNotAcceptable()\n\n       def reg_acceptor(self, accept, handler):\n           self._accepts[accept] = handler\n\n\n   async def handle_json(request):\n       # do json handling\n\n   async def handle_xml(request):\n       # do xml handling\n\n   chooser = AcceptChooser()\n   app.add_routes([web.get('/', chooser.do_route)])\n\n   chooser.reg_acceptor('application/json', handle_json)\n   chooser.reg_acceptor('application/xml', handle_xml)\n\n.. _aiohttp-web-static-file-handling:\n\nStatic file handling\n--------------------\n\nThe best way to handle static files (images, JavaScripts, CSS files\netc.) is using `Reverse Proxy`_ like `nginx`_ or `CDN`_ services.\n\n.. _Reverse Proxy: https://en.wikipedia.org/wiki/Reverse_proxy\n.. _nginx: https://nginx.org/\n.. _CDN: https://en.wikipedia.org/wiki/Content_delivery_network\n\nBut for development it's very convenient to handle static files by\naiohttp server itself.\n\nTo do it just register a new static route by\n:meth:`RouteTableDef.static` or :func:`static` calls::\n\n   app.add_routes([web.static('/prefix', path_to_static_folder)])\n\n   routes.static('/prefix', path_to_static_folder)\n\nWhen a directory is accessed within a static route then the server responses\nto client with ``HTTP/403 Forbidden`` by default. Displaying folder index\ninstead could be enabled with ``show_index`` parameter set to ``True``::\n\n   web.static('/prefix', path_to_static_folder, show_index=True)\n\nWhen a symlink that leads outside the static directory is accessed, the server\nresponds to the client with ``HTTP/404 Not Found`` by default. To allow the server to\nfollow symlinks that lead outside the static root, the parameter ``follow_symlinks``\nshould be set to ``True``::\n\n   web.static('/prefix', path_to_static_folder, follow_symlinks=True)\n\n.. caution::\n\n   Enabling ``follow_symlinks`` can be a security risk, and may lead to\n   a directory transversal attack. You do NOT need this option to follow symlinks\n   which point to somewhere else within the static directory, this option is only\n   used to break out of the security sandbox. Enabling this option is highly\n   discouraged, and only expected to be used for edge cases in a local\n   development setting where remote users do not have access to the server.\n\nWhen you want to enable cache busting,\nparameter ``append_version`` can be set to ``True``\n\nCache busting is the process of appending some form of file version hash\nto the filename of resources like JavaScript and CSS files.\nThe performance advantage of doing this is that we can tell the browser\nto cache these files indefinitely without worrying about the client not getting\nthe latest version when the file changes::\n\n   web.static('/prefix', path_to_static_folder, append_version=True)\n\nTemplate Rendering\n------------------\n\n:mod:`aiohttp.web` does not support template rendering out-of-the-box.\n\nHowever, there is a third-party library, :mod:`aiohttp_jinja2`, which is\nsupported by the *aiohttp* authors.\n\nUsing it is rather simple. First, setup a *jinja2 environment* with a call\nto :func:`aiohttp_jinja2.setup`::\n\n    app = web.Application()\n    aiohttp_jinja2.setup(app,\n        loader=jinja2.FileSystemLoader('/path/to/templates/folder'))\n\nAfter that you may use the template engine in your\n:ref:`handlers <aiohttp-web-handler>`. The most convenient way is to simply\nwrap your handlers with the  :func:`aiohttp_jinja2.template` decorator::\n\n    @aiohttp_jinja2.template('tmpl.jinja2')\n    async def handler(request):\n        return {'name': 'Andrew', 'surname': 'Svetlov'}\n\nIf you prefer the `Mako`_ template engine, please take a look at the\n`aiohttp_mako`_ library.\n\n.. warning::\n\n   :func:`aiohttp_jinja2.template` should be applied **before**\n   :meth:`RouteTableDef.get` decorator and family, e.g. it must be\n   the *first* (most *down* decorator in the chain)::\n\n\n      @routes.get('/path')\n      @aiohttp_jinja2.template('tmpl.jinja2')\n      async def handler(request):\n          return {'name': 'Andrew', 'surname': 'Svetlov'}\n\n\n.. _Mako: http://www.makotemplates.org/\n\n.. _aiohttp_mako: https://github.com/aio-libs/aiohttp_mako\n\n\n.. _aiohttp-web-websocket-read-same-task:\n\nReading from the same task in WebSockets\n----------------------------------------\n\nReading from the *WebSocket* (``await ws.receive()``) **must only** be\ndone inside the request handler *task*; however, writing\n(``ws.send_str(...)``) to the *WebSocket*, closing (``await\nws.close()``) and canceling the handler task may be delegated to other\ntasks. See also :ref:`FAQ section\n<aiohttp_faq_terminating_websockets>`.\n\n:mod:`aiohttp.web` creates an implicit :class:`asyncio.Task` for\nhandling every incoming request.\n\n.. note::\n\n   While :mod:`aiohttp.web` itself only supports *WebSockets* without\n   downgrading to *LONG-POLLING*, etc., our team supports SockJS_, an\n   aiohttp-based library for implementing SockJS-compatible server\n   code.\n\n.. _SockJS: https://github.com/aio-libs/sockjs\n\n\n.. warning::\n\n   Parallel reads from websocket are forbidden, there is no\n   possibility to call :meth:`WebSocketResponse.receive`\n   from two tasks.\n\n   See :ref:`FAQ section <aiohttp_faq_parallel_event_sources>` for\n   instructions how to solve the problem.\n\n\n.. _aiohttp-web-data-sharing:\n\nData Sharing aka No Singletons Please\n-------------------------------------\n\n:mod:`aiohttp.web` discourages the use of *global variables*, aka *singletons*.\nEvery variable should have its own context that is *not global*.\n\nGlobal variables are generally considered bad practice due to the complexity\nthey add in keeping track of state changes to variables.\n\n*aiohttp* does not use globals by design, which will reduce the number of bugs\nand/or unexpected behaviors for its users. For example, an i18n translated string\nbeing written for one request and then being served to another.\n\nSo, :class:`Application` and :class:`Request`\nsupport a :class:`collections.abc.MutableMapping` interface (i.e. they are\ndict-like objects), allowing them to be used as data stores.\n\n\n.. _aiohttp-web-data-sharing-app-config:\n\nApplication's config\n^^^^^^^^^^^^^^^^^^^^\n\nFor storing *global-like* variables, feel free to save them in an\n:class:`Application` instance::\n\n    app['my_private_key'] = data\n\nand get it back in the :term:`web-handler`::\n\n    async def handler(request):\n        data = request.app['my_private_key']\n\nRather than using :class:`str` keys, we recommend using :class:`AppKey`.\nThis is required for type safety (e.g. when checking with mypy)::\n\n    my_private_key = web.AppKey(\"my_private_key\", str)\n    app[my_private_key] = data\n\n    async def handler(request: web.Request):\n        data = request.app[my_private_key]\n        # reveal_type(data) -> str\n\nIn case of :ref:`nested applications\n<aiohttp-web-nested-applications>` the desired lookup strategy could\nbe the following:\n\n1. Search the key in the current nested application.\n2. If the key is not found continue searching in the parent application(s).\n\nFor this please use :attr:`Request.config_dict` read-only property::\n\n    async def handler(request):\n        data = request.config_dict[my_private_key]\n\nThe app object can be used in this way to reuse a database connection or anything\nelse needed throughout the application.\n\nSee this reference section for more detail: :ref:`aiohttp-web-app-and-router`.\n\nRequest's storage\n^^^^^^^^^^^^^^^^^\n\nVariables that are only needed for the lifetime of a :class:`Request`, can be\nstored in a :class:`Request`::\n\n    async def handler(request):\n      request['my_private_key'] = \"data\"\n      ...\n\nThis is mostly useful for :ref:`aiohttp-web-middlewares` and\n:ref:`aiohttp-web-signals` handlers to store data for further processing by the\nnext handlers in the chain.\n\nResponse's storage\n^^^^^^^^^^^^^^^^^^\n\n:class:`StreamResponse` and :class:`Response` objects\nalso support :class:`collections.abc.MutableMapping` interface. This is useful\nwhen you want to share data with signals and middlewares once all the work in\nthe handler is done::\n\n    async def handler(request):\n      [ do all the work ]\n      response['my_metric'] = 123\n      return response\n\n\nNaming hint\n^^^^^^^^^^^\n\nTo avoid clashing with other *aiohttp* users and third-party libraries, please\nchoose a unique key name for storing data.\n\nIf your code is published on PyPI, then the project name is most likely unique\nand safe to use as the key.\nOtherwise, something based on your company name/url would be satisfactory (i.e.\n``org.company.app``).\n\n\n.. _aiohttp-web-contextvars:\n\n\nContextVars support\n-------------------\n\nAsyncio has :mod:`Context Variables <contextvars>` as a context-local storage\n(a generalization of thread-local concept that works with asyncio tasks also).\n\n\n*aiohttp* server supports it in the following way:\n\n* A server inherits the current task's context used when creating it.\n  :func:`aiohttp.web.run_app()` runs a task for handling all underlying jobs running\n  the app, but alternatively :ref:`aiohttp-web-app-runners` can be used.\n\n* Application initialization / finalization events (:attr:`Application.cleanup_ctx`,\n  :attr:`Application.on_startup` and :attr:`Application.on_shutdown`,\n  :attr:`Application.on_cleanup`) are executed inside the same context.\n\n  E.g. all context modifications made on application startup are visible on teardown.\n\n* On every request handling *aiohttp* creates a context copy. :term:`web-handler` has\n  all variables installed on initialization stage. But the context modification made by\n  a handler or middleware is invisible to another HTTP request handling call.\n\nAn example of context vars usage::\n\n    from contextvars import ContextVar\n\n    from aiohttp import web\n\n    VAR = ContextVar('VAR', default='default')\n\n\n    async def coro():\n        return VAR.get()\n\n\n    async def handler(request):\n        var = VAR.get()\n        VAR.set('handler')\n        ret = await coro()\n        return web.Response(text='\\n'.join([var,\n                                            ret]))\n\n\n    async def on_startup(app):\n        print('on_startup', VAR.get())\n        VAR.set('on_startup')\n\n\n    async def on_cleanup(app):\n        print('on_cleanup', VAR.get())\n        VAR.set('on_cleanup')\n\n\n    async def init():\n        print('init', VAR.get())\n        VAR.set('init')\n        app = web.Application()\n        app.router.add_get('/', handler)\n\n        app.on_startup.append(on_startup)\n        app.on_cleanup.append(on_cleanup)\n        return app\n\n\n    web.run_app(init())\n    print('done', VAR.get())\n\n.. versionadded:: 3.5\n\n\n.. _aiohttp-web-middlewares:\n\nMiddlewares\n-----------\n\n:mod:`aiohttp.web` provides a powerful mechanism for customizing\n:ref:`request handlers<aiohttp-web-handler>` via *middlewares*.\n\nA *middleware* is a coroutine that can modify either the request or\nresponse. For example, here's a simple *middleware* which appends\n``' wink'`` to the response::\n\n    from aiohttp.web import middleware\n\n    @middleware\n    async def middleware(request, handler):\n        resp = await handler(request)\n        resp.text = resp.text + ' wink'\n        return resp\n\n.. note::\n\n   The example won't work with streamed responses or websockets\n\nEvery *middleware* should accept two parameters, a :class:`request\n<Request>` instance and a *handler*, and return the response or raise\nan exception. If the exception is not an instance of\n:exc:`HTTPException` it is converted to ``500``\n:exc:`HTTPInternalServerError` after processing the\nmiddlewares chain.\n\n.. warning::\n\n   Second argument should be named *handler* exactly.\n\nWhen creating an :class:`Application`, these *middlewares* are passed to\nthe keyword-only ``middlewares`` parameter::\n\n   app = web.Application(middlewares=[middleware_1,\n                                      middleware_2])\n\nInternally, a single :ref:`request handler <aiohttp-web-handler>` is constructed\nby applying the middleware chain to the original handler in reverse order,\nand is called by the :class:`~aiohttp.web.RequestHandler` as a regular *handler*.\n\nSince *middlewares* are themselves coroutines, they may perform extra\n``await`` calls when creating a new handler, e.g. call database etc.\n\n*Middlewares* usually call the handler, but they may choose to ignore it,\ne.g. displaying *403 Forbidden page* or raising :exc:`HTTPForbidden` exception\nif the user does not have permissions to access the underlying resource.\nThey may also render errors raised by the handler, perform some pre- or\npost-processing like handling *CORS* and so on.\n\nThe following code demonstrates middlewares execution order::\n\n   from aiohttp import web\n\n   async def test(request):\n       print('Handler function called')\n       return web.Response(text=\"Hello\")\n\n   @web.middleware\n   async def middleware1(request, handler):\n       print('Middleware 1 called')\n       response = await handler(request)\n       print('Middleware 1 finished')\n       return response\n\n   @web.middleware\n   async def middleware2(request, handler):\n       print('Middleware 2 called')\n       response = await handler(request)\n       print('Middleware 2 finished')\n       return response\n\n\n   app = web.Application(middlewares=[middleware1, middleware2])\n   app.router.add_get('/', test)\n   web.run_app(app)\n\nProduced output::\n\n   Middleware 1 called\n   Middleware 2 called\n   Handler function called\n   Middleware 2 finished\n   Middleware 1 finished\n\nExample\n^^^^^^^\n\nA common use of middlewares is to implement custom error pages.  The following\nexample will render 404 errors using a JSON response, as might be appropriate\na JSON REST service::\n\n    from aiohttp import web\n\n    @web.middleware\n    async def error_middleware(request, handler):\n        try:\n            response = await handler(request)\n            if response.status != 404:\n                return response\n            message = response.message\n        except web.HTTPException as ex:\n            if ex.status != 404:\n                raise\n            message = ex.reason\n        return web.json_response({'error': message})\n\n    app = web.Application(middlewares=[error_middleware])\n\n\nMiddleware Factory\n^^^^^^^^^^^^^^^^^^\n\nA *middleware factory* is a function that creates a middleware with passed arguments. For example, here's a trivial *middleware factory*::\n\n    def middleware_factory(text):\n        @middleware\n        async def sample_middleware(request, handler):\n            resp = await handler(request)\n            resp.text = resp.text + text\n            return resp\n        return sample_middleware\n\nRemember that contrary to regular middlewares you need the result of a middleware factory not the function itself. So when passing a middleware factory to an app you actually need to call it::\n\n    app = web.Application(middlewares=[middleware_factory(' wink')])\n\n.. _aiohttp-web-signals:\n\nSignals\n-------\n\nAlthough :ref:`middlewares <aiohttp-web-middlewares>` can customize\n:ref:`request handlers<aiohttp-web-handler>` before or after a :class:`Response`\nhas been prepared, they can't customize a :class:`Response` **while** it's\nbeing prepared. For this :mod:`aiohttp.web` provides *signals*.\n\nFor example, a middleware can only change HTTP headers for *unprepared*\nresponses (see :meth:`StreamResponse.prepare`), but sometimes we\nneed a hook for changing HTTP headers for streamed responses and WebSockets.\nThis can be accomplished by subscribing to the\n:attr:`Application.on_response_prepare` signal, which is called after default\nheaders have been computed and directly before headers are sent::\n\n    async def on_prepare(request, response):\n        response.headers['My-Header'] = 'value'\n\n    app.on_response_prepare.append(on_prepare)\n\n\nAdditionally, the :attr:`Application.on_startup` and\n:attr:`Application.on_cleanup` signals can be subscribed to for\napplication component setup and tear down accordingly.\n\nThe following example will properly initialize and dispose an asyncpg connection\nengine::\n\n    from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine\n\n    pg_engine = web.AppKey(\"pg_engine\", AsyncEngine)\n\n    async def create_pg(app):\n        app[pg_engine] = await create_async_engine(\n            \"postgresql+asyncpg://postgre:@localhost:5432/postgre\"\n        )\n\n    async def dispose_pg(app):\n        await app[pg_engine].dispose()\n\n    app.on_startup.append(create_pg)\n    app.on_cleanup.append(dispose_pg)\n\n\nSignal handlers should not return a value but may modify incoming mutable\nparameters.\n\nSignal handlers will be run sequentially, in order they were\nadded. All handlers must be asynchronous since *aiohttp* 3.0.\n\n.. _aiohttp-web-cleanup-ctx:\n\nCleanup Context\n---------------\n\nBare :attr:`Application.on_startup` / :attr:`Application.on_cleanup`\npair still has a pitfall: signals handlers are independent on each other.\n\nE.g. we have ``[create_pg, create_redis]`` in *startup* signal and\n``[dispose_pg, dispose_redis]`` in *cleanup*.\n\nIf, for example, ``create_pg(app)`` call fails ``create_redis(app)``\nis not called. But on application cleanup both ``dispose_pg(app)`` and\n``dispose_redis(app)`` are still called: *cleanup signal* has no\nknowledge about startup/cleanup pairs and their execution state.\n\n\nThe solution is :attr:`Application.cleanup_ctx` usage::\n\n    async def pg_engine(app: web.Application):\n        app[pg_engine] = await create_async_engine(\n            \"postgresql+asyncpg://postgre:@localhost:5432/postgre\"\n        )\n        yield\n        await app[pg_engine].dispose()\n\n    app.cleanup_ctx.append(pg_engine)\n\nThe attribute is a list of *asynchronous generators*, a code *before*\n``yield`` is an initialization stage (called on *startup*), a code\n*after* ``yield`` is executed on *cleanup*. The generator must have only\none ``yield``.\n\n*aiohttp* guarantees that *cleanup code* is called if and only if\n*startup code* was successfully finished.\n\n.. versionadded:: 3.1\n\n.. _aiohttp-web-nested-applications:\n\nNested applications\n-------------------\n\nSub applications are designed for solving the problem of the big\nmonolithic code base.\nLet's assume we have a project with own business logic and tools like\nadministration panel and debug toolbar.\n\nAdministration panel is a separate application by its own nature but all\ntoolbar URLs are served by prefix like ``/admin``.\n\nThus we'll create a totally separate application named ``admin`` and\nconnect it to main app with prefix by\n:meth:`Application.add_subapp`::\n\n   admin = web.Application()\n   # setup admin routes, signals and middlewares\n\n   app.add_subapp('/admin/', admin)\n\nMiddlewares and signals from ``app`` and ``admin`` are chained.\n\nIt means that if URL is ``'/admin/something'`` middlewares from\n``app`` are applied first and ``admin.middlewares`` are the next in\nthe call chain.\n\nThe same is going for\n:attr:`Application.on_response_prepare` signal -- the\nsignal is delivered to both top level ``app`` and ``admin`` if\nprocessing URL is routed to ``admin`` sub-application.\n\nCommon signals like :attr:`Application.on_startup`,\n:attr:`Application.on_shutdown` and\n:attr:`Application.on_cleanup` are delivered to all\nregistered sub-applications. The passed parameter is sub-application\ninstance, not top-level application.\n\n\nThird level sub-applications can be nested into second level ones --\nthere are no limitation for nesting level.\n\nUrl reversing for sub-applications should generate urls with proper prefix.\n\nBut for getting URL sub-application's router should be used::\n\n   admin = web.Application()\n   admin.add_routes([web.get('/resource', handler, name='name')])\n\n   app.add_subapp('/admin/', admin)\n\n   url = admin.router['name'].url_for()\n\nThe generated ``url`` from example will have a value\n``URL('/admin/resource')``.\n\nIf main application should do URL reversing for sub-application it could\nuse the following explicit technique::\n\n   admin = web.Application()\n   admin_key = web.AppKey('admin_key', web.Application)\n   admin.add_routes([web.get('/resource', handler, name='name')])\n\n   app.add_subapp('/admin/', admin)\n   app[admin_key] = admin\n\n   async def handler(request: web.Request):  # main application's handler\n       admin = request.app[admin_key]\n       url = admin.router['name'].url_for()\n\n.. _aiohttp-web-expect-header:\n\n*Expect* Header\n---------------\n\n:mod:`aiohttp.web` supports *Expect* header. By default it sends\n``HTTP/1.1 100 Continue`` line to client, or raises\n:exc:`HTTPExpectationFailed` if header value is not equal to\n\"100-continue\". It is possible to specify custom *Expect* header\nhandler on per route basis. This handler gets called if *Expect*\nheader exist in request after receiving all headers and before\nprocessing application's :ref:`aiohttp-web-middlewares` and\nroute handler. Handler can return *None*, in that case the request\nprocessing continues as usual. If handler returns an instance of class\n:class:`StreamResponse`, *request handler* uses it as response. Also\nhandler can raise a subclass of :exc:`HTTPException`. In this case all\nfurther processing will not happen and client will receive appropriate\nhttp response.\n\n.. note::\n    A server that does not understand or is unable to comply with any of the\n    expectation values in the Expect field of a request MUST respond with\n    appropriate error status. The server MUST respond with a 417\n    (Expectation Failed) status if any of the expectations cannot be met or,\n    if there are other problems with the request, some other 4xx status.\n\n    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20\n\nIf all checks pass, the custom handler *must* write a *HTTP/1.1 100 Continue*\nstatus code before returning.\n\nThe following example shows how to setup a custom handler for the *Expect*\nheader::\n\n   async def check_auth(request):\n       if request.version != aiohttp.HttpVersion11:\n           return\n\n       if request.headers.get('EXPECT') != '100-continue':\n           raise HTTPExpectationFailed(text=\"Unknown Expect: %s\" % expect)\n\n       if request.headers.get('AUTHORIZATION') is None:\n           raise HTTPForbidden()\n\n       request.transport.write(b\"HTTP/1.1 100 Continue\\r\\n\\r\\n\")\n\n   async def hello(request):\n       return web.Response(body=b\"Hello, world\")\n\n   app = web.Application()\n   app.add_routes([web.add_get('/', hello, expect_handler=check_auth)])\n\n.. _aiohttp-web-custom-resource:\n\nCustom resource implementation\n------------------------------\n\nTo register custom resource use :meth:`~aiohttp.web.UrlDispatcher.register_resource`.\nResource instance must implement `AbstractResource` interface.\n\n.. _aiohttp-web-app-runners:\n\nApplication runners\n-------------------\n\n:func:`run_app` provides a simple *blocking* API for running an\n:class:`Application`.\n\nFor starting the application *asynchronously* or serving on multiple\nHOST/PORT :class:`AppRunner` exists.\n\nThe simple startup code for serving HTTP site on ``'localhost'``, port\n``8080`` looks like::\n\n    runner = web.AppRunner(app)\n    await runner.setup()\n    site = web.TCPSite(runner, 'localhost', 8080)\n    await site.start()\n\n    while True:\n        await asyncio.sleep(3600)  # sleep forever\n\nTo stop serving call :meth:`AppRunner.cleanup`::\n\n    await runner.cleanup()\n\n.. versionadded:: 3.0\n\n.. _aiohttp-web-graceful-shutdown:\n\nGraceful shutdown\n------------------\n\nStopping *aiohttp web server* by just closing all connections is not\nalways satisfactory.\n\nWhen aiohttp is run with :func:`run_app`, it will attempt a graceful shutdown\nby following these steps (if using a :ref:`runner <aiohttp-web-app-runners>`,\nthen calling :meth:`AppRunner.cleanup` will perform these steps, excluding\nstep 7).\n\n1. Stop each site listening on sockets, so new connections will be rejected.\n2. Close idle keep-alive connections (and set active ones to close upon completion).\n3. Call the :attr:`Application.on_shutdown` signal. This should be used to shutdown\n   long-lived connections, such as websockets (see below).\n4. Wait a short time for running handlers to complete. This allows any pending handlers\n   to complete successfully. The timeout can be adjusted with ``shutdown_timeout``\n   in :func:`run_app`.\n5. Close any remaining connections and cancel their handlers. It will wait on the\n   canceling handlers for a short time, again adjustable with ``shutdown_timeout``.\n6. Call the :attr:`Application.on_cleanup` signal. This should be used to cleanup any\n   resources (such as DB connections). This includes completing the\n   :ref:`cleanup contexts<aiohttp-web-cleanup-ctx>` which may be used to ensure\n   background tasks are completed successfully (see\n   :ref:`handler cancellation<web-handler-cancellation>` or aiojobs_ for examples).\n7. Cancel any remaining tasks and wait on them to complete.\n\nWebsocket shutdown\n^^^^^^^^^^^^^^^^^^\n\nOne problem is if the application supports :term:`websockets <websocket>` or\n*data streaming* it most likely has open connections at server shutdown time.\n\nThe *library* has no knowledge how to close them gracefully but a developer can\nhelp by registering an :attr:`Application.on_shutdown` signal handler.\n\nA developer should keep a list of opened connections\n(:class:`Application` is a good candidate).\n\nThe following :term:`websocket` snippet shows an example of a websocket handler::\n\n    from aiohttp import web\n    import weakref\n\n    app = web.Application()\n    websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n    app[websockets] = weakref.WeakSet()\n\n    async def websocket_handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        request.app[websockets].add(ws)\n        try:\n            async for msg in ws:\n                ...\n        finally:\n            request.app[websockets].discard(ws)\n\n        return ws\n\nThen the signal handler may look like::\n\n    from aiohttp import WSCloseCode\n\n    async def on_shutdown(app):\n        for ws in set(app[websockets]):\n            await ws.close(code=WSCloseCode.GOING_AWAY, message=\"Server shutdown\")\n\n    app.on_shutdown.append(on_shutdown)\n\n.. _aiohttp-web-ceil-absolute-timeout:\n\nCeil of absolute timeout value\n------------------------------\n\n*aiohttp* **ceils** internal timeout values if the value is equal or\ngreater than 5 seconds. The timeout expires at the next integer second\ngreater than ``current_time + timeout``.\n\nMore details about ceiling absolute timeout values is available here\n:ref:`aiohttp-client-timeouts`.\n\nThe default threshold can be configured at :class:`aiohttp.web.Application`\nlevel using the ``handler_args`` parameter.\n\n.. code-block:: python3\n\n    app = web.Application(handler_args={\"timeout_ceil_threshold\": 1})\n\n.. _aiohttp-web-background-tasks:\n\nBackground tasks\n-----------------\n\nSometimes there's a need to perform some asynchronous operations just\nafter application start-up.\n\nEven more, in some sophisticated systems there could be a need to run some\nbackground tasks in the event loop along with the application's request\nhandler. Such as listening to message queue or other network message/event\nsources (e.g. ZeroMQ, Redis Pub/Sub, AMQP, etc.) to react to received messages\nwithin the application.\n\nFor example the background task could listen to ZeroMQ on\n``zmq.SUB`` socket, process and forward retrieved messages to\nclients connected via WebSocket that are stored somewhere in the\napplication (e.g. in the ``application['websockets']`` list).\n\nTo run such short and long running background tasks aiohttp provides an\nability to register :attr:`Application.on_startup` signal handler(s) that\nwill run along with the application's request handler.\n\nFor example there's a need to run one quick task and two long running\ntasks that will live till the application is alive. The appropriate\nbackground tasks could be registered as an :attr:`Application.on_startup`\nsignal handler or :attr:`Application.cleanup_ctx` as shown in the example\nbelow::\n\n  async def listen_to_redis(app: web.Application):\n      client = redis.from_url(\"redis://localhost:6379\")\n      channel = \"news\"\n      async with client.pubsub() as pubsub:\n          await pubsub.subscribe(channel)\n          while True:\n              msg = await pubsub.get_message(ignore_subscribe_messages=True)\n              if msg is not None:\n                  for ws in app[\"websockets\"]:\n                      await ws.send_str(\"{}: {}\".format(channel, msg))\n\n\n  async def background_tasks(app):\n      app[redis_listener] = asyncio.create_task(listen_to_redis(app))\n\n      yield\n\n      app[redis_listener].cancel()\n      with contextlib.suppress(asyncio.CancelledError):\n          await app[redis_listener]\n\n\n  app = web.Application()\n  redis_listener = web.AppKey(\"redis_listener\", asyncio.Task[None])\n  app.cleanup_ctx.append(background_tasks)\n  web.run_app(app)\n\n\nThe task ``listen_to_redis`` will run forever.\nTo shut it down correctly :attr:`Application.on_cleanup` signal handler\nmay be used to send a cancellation to it.\n\n.. _aiohttp-web-complex-applications:\n\nComplex Applications\n^^^^^^^^^^^^^^^^^^^^\n\nSometimes aiohttp is not the sole part of an application and additional\ntasks/processes may need to be run alongside the aiohttp :class:`Application`.\n\nGenerally, the best way to achieve this is to use :func:`aiohttp.web.run_app`\nas the entry point for the program. Other tasks can then be run via\n:attr:`Application.startup` and :attr:`Application.on_cleanup`. By having the\n:class:`Application` control the lifecycle of the entire program, the code\nwill be more robust and ensure that the tasks are started and stopped along\nwith the application.\n\nFor example, running a long-lived task alongside the :class:`Application`\ncan be done with a :ref:`aiohttp-web-cleanup-ctx` function like::\n\n\n  async def run_other_task(_app):\n      task = asyncio.create_task(other_long_task())\n\n      yield\n\n      task.cancel()\n      with suppress(asyncio.CancelledError):\n          await task  # Ensure any exceptions etc. are raised.\n\n  app.cleanup_ctx.append(run_other_task)\n\n\nOr a separate process can be run with something like::\n\n\n  async def run_process(_app):\n      proc = await asyncio.create_subprocess_exec(path)\n\n      yield\n\n      if proc.returncode is None:\n          proc.terminate()\n      await proc.wait()\n\n  app.cleanup_ctx.append(run_process)\n\n\nHandling error pages\n--------------------\n\nPages like *404 Not Found* and *500 Internal Error* could be handled\nby custom middleware, see :ref:`polls demo <aiohttpdemos:aiohttp-demos-polls-middlewares>`\nfor example.\n\n.. _aiohttp-web-forwarded-support:\n\nDeploying behind a Proxy\n------------------------\n\nAs discussed in :ref:`aiohttp-deployment` the preferable way is\ndeploying *aiohttp* web server behind a *Reverse Proxy Server* like\n:term:`nginx` for production usage.\n\nIn this way properties like :attr:`BaseRequest.scheme`\n:attr:`BaseRequest.host` and :attr:`BaseRequest.remote` are\nincorrect.\n\nReal values should be given from proxy server, usually either\n``Forwarded`` or old-fashion ``X-Forwarded-For``,\n``X-Forwarded-Host``, ``X-Forwarded-Proto`` HTTP headers are used.\n\n*aiohttp* does not take *forwarded* headers into account by default\nbecause it produces *security issue*: HTTP client might add these\nheaders too, pushing non-trusted data values.\n\nThat's why *aiohttp server* should setup *forwarded* headers in custom\nmiddleware in tight conjunction with *reverse proxy configuration*.\n\nFor changing :attr:`BaseRequest.scheme` :attr:`BaseRequest.host`\n:attr:`BaseRequest.remote` and :attr:`BaseRequest.client_max_size`\nthe middleware might use :meth:`BaseRequest.clone`.\n\n.. seealso::\n\n   https://github.com/aio-libs/aiohttp-remotes provides secure helpers\n   for modifying *scheme*, *host* and *remote* attributes according\n   to ``Forwarded`` and ``X-Forwarded-*`` HTTP headers.\n\nSwagger support\n---------------\n\n`aiohttp-swagger <https://github.com/cr0hn/aiohttp-swagger>`_ is a\nlibrary that allow to add Swagger documentation and embed the\nSwagger-UI into your :mod:`aiohttp.web` project.\n\nCORS support\n------------\n\n:mod:`aiohttp.web` itself does not support `Cross-Origin Resource\nSharing <https://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`_, but\nthere is an aiohttp plugin for it:\n`aiohttp_cors <https://github.com/aio-libs/aiohttp_cors>`_.\n\n\nDebug Toolbar\n-------------\n\n`aiohttp-debugtoolbar`_ is a very useful library that provides a\ndebugging toolbar while you're developing an :mod:`aiohttp.web`\napplication.\n\nInstall it with ``pip``:\n\n.. code-block:: shell\n\n    $ pip install aiohttp_debugtoolbar\n\n\nJust call :func:`aiohttp_debugtoolbar.setup`::\n\n    import aiohttp_debugtoolbar\n    from aiohttp_debugtoolbar import toolbar_middleware_factory\n\n    app = web.Application()\n    aiohttp_debugtoolbar.setup(app)\n\nThe toolbar is ready to use. Enjoy!!!\n\n.. _aiohttp-debugtoolbar: https://github.com/aio-libs/aiohttp_debugtoolbar\n\n\nDev Tools\n---------\n\n`aiohttp-devtools`_ provides a couple of tools to simplify development of\n:mod:`aiohttp.web` applications.\n\n\nInstall with ``pip``:\n\n.. code-block:: shell\n\n    $ pip install aiohttp-devtools\n\n* ``runserver`` provides a development server with auto-reload,\n  live-reload, static file serving.\n* ``start`` is a `cookiecutter command which does the donkey work\n  of creating new :mod:`aiohttp.web` Applications.\n\nDocumentation and a complete tutorial of creating and running an app\nlocally are available at `aiohttp-devtools`_.\n\n.. _aiohttp-devtools: https://github.com/aio-libs/aiohttp-devtools\n",
            "header": {
                "file": {
                    "path": "docs/web_advanced.rst"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "dc94bea3",
                "new_commit_hash": "070bae34",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/docs/web_advanced.rst"
            },
            "plus_file": {
                "path": "b/docs/web_advanced.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1064,
                            "length": 13
                        },
                        "new_range": {
                            "start": 1064,
                            "length": 10
                        },
                        "section": "below::"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "      async with client.pubsub() as pubsub:",
                                "type": "context"
                            },
                            {
                                "content": "          await pubsub.subscribe(channel)",
                                "type": "context"
                            },
                            {
                                "content": "          while True:",
                                "type": "context"
                            },
                            {
                                "content": "              try:",
                                "type": "deleted"
                            },
                            {
                                "content": "                  msg = await pubsub.get_message(ignore_subscribe_messages=True)",
                                "type": "deleted"
                            },
                            {
                                "content": "                  if msg is not None:",
                                "type": "deleted"
                            },
                            {
                                "content": "                      for ws in app[\"websockets\"]:",
                                "type": "deleted"
                            },
                            {
                                "content": "                          await ws.send_str(\"{}: {}\".format(channel, msg))",
                                "type": "deleted"
                            },
                            {
                                "content": "              except asyncio.CancelledError:",
                                "type": "deleted"
                            },
                            {
                                "content": "                  break",
                                "type": "deleted"
                            },
                            {
                                "content": "              msg = await pubsub.get_message(ignore_subscribe_messages=True)",
                                "type": "added"
                            },
                            {
                                "content": "              if msg is not None:",
                                "type": "added"
                            },
                            {
                                "content": "                  for ws in app[\"websockets\"]:",
                                "type": "added"
                            },
                            {
                                "content": "                      await ws.send_str(\"{}: {}\".format(channel, msg))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "  async def background_tasks(app):",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1079,
                            "length": 7
                        },
                        "new_range": {
                            "start": 1076,
                            "length": 8
                        },
                        "section": "below::"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "      yield",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "      app[redis_listener].cancel()",
                                "type": "context"
                            },
                            {
                                "content": "      await app[redis_listener]",
                                "type": "deleted"
                            },
                            {
                                "content": "      with contextlib.suppress(asyncio.CancelledError):",
                                "type": "added"
                            },
                            {
                                "content": "          await app[redis_listener]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "  app = web.Application()",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport gc\nfrom typing import AsyncIterator, Callable, Iterator, NoReturn\nfrom unittest import mock\n\nimport pytest\n\nfrom aiohttp import log, web\nfrom aiohttp.abc import AbstractAccessLogger, AbstractRouter\nfrom aiohttp.helpers import DEBUG\nfrom aiohttp.test_utils import make_mocked_coro\nfrom aiohttp.typedefs import Handler\n\n\nasync def test_app_ctor() -> None:\n    loop = asyncio.get_event_loop()\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(loop=loop)\n    with pytest.warns(DeprecationWarning):\n        assert loop is app.loop\n    assert app.logger is log.web_logger\n\n\ndef test_app_call() -> None:\n    app = web.Application()\n    assert app is app()\n\n\ndef test_app_default_loop() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is None\n\n\nasync def test_set_loop() -> None:\n    loop = asyncio.get_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n\ndef test_set_loop_default_loop() -> None:\n    loop = asyncio.new_event_loop()\n    asyncio.set_event_loop(loop)\n    app = web.Application()\n    try:\n        app._set_loop(None)\n        with pytest.warns(DeprecationWarning):\n            assert app.loop is loop\n        asyncio.set_event_loop(None)\n\n    finally:\n        # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n        loop.stop()\n        loop.run_forever()\n        loop.close()\n        gc.collect()\n\n\ndef test_set_loop_with_different_loops() -> None:\n    loop = asyncio.new_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n    with pytest.raises(RuntimeError):\n        app._set_loop(loop=object())\n\n    # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n    loop.stop()\n    loop.run_forever()\n    loop.close()\n    gc.collect()\n\n\n@pytest.mark.parametrize(\"debug\", [True, False])\nasync def test_app_make_handler_debug_exc(mocker, debug) -> None:\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(debug=debug)\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    with pytest.warns(DeprecationWarning):\n        assert app.debug == debug\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=debug,\n    )\n\n\nasync def test_app_make_handler_args(mocker) -> None:\n    app = web.Application(handler_args={\"test\": True})\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n        test=True,\n    )\n\n\nasync def test_app_make_handler_access_log_class(mocker) -> None:\n    class Logger:\n        pass\n\n    app = web.Application()\n\n    with pytest.raises(TypeError):\n        app._make_handler(access_log_class=Logger)\n\n    class Logger(AbstractAccessLogger):\n        def log(self, request, response, time):\n            self.logger.info(\"msg\")\n\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n    app = web.Application(handler_args={\"access_log_class\": Logger})\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n\nasync def test_app_make_handler_raises_deprecation_warning() -> None:\n    app = web.Application()\n\n    with pytest.warns(DeprecationWarning):\n        app.make_handler()\n\n\nasync def test_app_register_on_finish() -> None:\n    app = web.Application()\n    cb1 = make_mocked_coro(None)\n    cb2 = make_mocked_coro(None)\n    app.on_cleanup.append(cb1)\n    app.on_cleanup.append(cb2)\n    app.freeze()\n    await app.cleanup()\n    cb1.assert_called_once_with(app)\n    cb2.assert_called_once_with(app)\n\n\nasync def test_app_register_coro() -> None:\n    app = web.Application()\n    fut = asyncio.get_event_loop().create_future()\n\n    async def cb(app: web.Application) -> None:\n        await asyncio.sleep(0.001)\n        fut.set_result(123)\n\n    app.on_cleanup.append(cb)\n    app.freeze()\n    await app.cleanup()\n    assert fut.done()\n    assert 123 == fut.result()\n\n\ndef test_non_default_router() -> None:\n    router = mock.Mock(spec=AbstractRouter)\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(router=router)\n    assert router is app.router\n\n\ndef test_logging() -> None:\n    logger = mock.Mock()\n    app = web.Application()\n    app.logger = logger\n    assert app.logger is logger\n\n\nasync def test_on_shutdown() -> None:\n    app = web.Application()\n    called = False\n\n    async def on_shutdown(app_param: web.Application) -> None:\n        nonlocal called\n        assert app is app_param\n        called = True\n\n    app.on_shutdown.append(on_shutdown)\n    app.freeze()\n    await app.shutdown()\n    assert called\n\n\nasync def test_on_startup() -> None:\n    app = web.Application()\n\n    long_running1_called = False\n    long_running2_called = False\n    all_long_running_called = False\n\n    async def long_running1(app_param: web.Application) -> None:\n        nonlocal long_running1_called\n        assert app is app_param\n        long_running1_called = True\n\n    async def long_running2(app_param: web.Application) -> None:\n        nonlocal long_running2_called\n        assert app is app_param\n        long_running2_called = True\n\n    async def on_startup_all_long_running(app_param: web.Application) -> None:\n        nonlocal all_long_running_called\n        assert app is app_param\n        all_long_running_called = True\n        await asyncio.gather(long_running1(app_param), long_running2(app_param))\n\n    app.on_startup.append(on_startup_all_long_running)\n    app.freeze()\n\n    await app.startup()\n    assert long_running1_called\n    assert long_running2_called\n    assert all_long_running_called\n\n\ndef test_appkey() -> None:\n    key = web.AppKey(\"key\", str)\n    app = web.Application()\n    app[key] = \"value\"\n    assert app[key] == \"value\"\n    assert len(app) == 1\n    del app[key]\n    assert len(app) == 0\n\n\ndef test_appkey_repr_concrete() -> None:\n    key = web.AppKey(\"key\", int)\n    assert repr(key) in (\n        \"<AppKey(__channelexec__.key, type=int)>\",  # pytest-xdist\n        \"<AppKey(__main__.key, type=int)>\",\n    )\n    key2 = web.AppKey(\"key\", web.Request)\n    assert repr(key2) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=aiohttp.web_request.Request)>\",\n        \"<AppKey(__main__.key, type=aiohttp.web_request.Request)>\",\n    )\n\n\ndef test_appkey_repr_nonconcrete() -> None:\n    key = web.AppKey(\"key\", Iterator[int])\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_appkey_repr_annotated() -> None:\n    key = web.AppKey[Iterator[int]](\"key\")\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_app_str_keys() -> None:\n    app = web.Application()\n    with pytest.warns(\n        UserWarning, match=r\"web_advanced\\.html#application-s-config\"\n    ) as checker:\n        app[\"key\"] = \"value\"\n        # Check that the error is emitted at the call site (stacklevel=2)\n        assert checker[0].filename == __file__\n    assert app[\"key\"] == \"value\"\n\n\ndef test_app_get() -> None:\n    key = web.AppKey(\"key\", int)\n    app = web.Application()\n    assert app.get(key, \"foo\") == \"foo\"\n    app[key] = 5\n    assert app.get(key, \"foo\") == 5\n\n\ndef test_app_freeze() -> None:\n    app = web.Application()\n    subapp = mock.Mock()\n    subapp._middlewares = ()\n    app._subapps.append(subapp)\n\n    app.freeze()\n    assert subapp.freeze.called\n\n    app.freeze()\n    assert len(subapp.freeze.call_args_list) == 1\n\n\ndef test_equality() -> None:\n    app1 = web.Application()\n    app2 = web.Application()\n\n    assert app1 == app1\n    assert app1 != app2\n\n\ndef test_app_run_middlewares() -> None:\n\n    root = web.Application()\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is False\n\n    @web.middleware\n    async def middleware(request: web.Request, handler: Handler) -> web.StreamResponse:\n        return await handler(request)  # pragma: no cover\n\n    root = web.Application(middlewares=[middleware])\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n    root = web.Application()\n    sub = web.Application(middlewares=[middleware])\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n\ndef test_subapp_pre_frozen_after_adding() -> None:\n    app = web.Application()\n    subapp = web.Application()\n\n    app.add_subapp(\"/prefix\", subapp)\n    assert subapp.pre_frozen\n    assert not subapp.frozen\n\n\ndef test_app_inheritance() -> None:\n    with pytest.warns(DeprecationWarning):\n\n        class A(web.Application):  # type: ignore[misc]\n            pass\n\n\n@pytest.mark.skipif(not DEBUG, reason=\"The check is applied in DEBUG mode only\")\ndef test_app_custom_attr() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        app.custom = None  # type: ignore[attr-defined]\n\n\nasync def test_cleanup_ctx() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_startup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            if fail:\n                raise exc\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    with pytest.raises(Exception) as ctx:\n        await app.startup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_cleanup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise exc\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(Exception) as ctx:\n        await app.cleanup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_cleanup_after_exception() -> None:\n    app = web.Application()\n    ctx_state = None\n\n    async def success_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_state\n        ctx_state = \"START\"\n        yield\n        ctx_state = \"CLEAN\"\n\n    async def fail_ctx(app: web.Application) -> AsyncIterator[NoReturn]:\n        raise Exception()\n        yield\n\n    app.cleanup_ctx.append(success_ctx)\n    app.cleanup_ctx.append(fail_ctx)\n    runner = web.AppRunner(app)\n    try:\n        with pytest.raises(Exception):\n            await runner.setup()\n    finally:\n        await runner.cleanup()\n\n    assert ctx_state == \"CLEAN\"\n\n\nasync def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:\n    app = web.Application()\n    out = []\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise Exception(\"fail_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3, True))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(web.CleanupError) as ctx:\n        await app.cleanup()\n    exc = ctx.value\n    assert len(exc.exceptions) == 2\n    assert str(exc.exceptions[0]) == \"fail_3\"\n    assert str(exc.exceptions[1]) == \"fail_2\"\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_multiple_yields() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            yield None\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\"]\n    with pytest.raises(RuntimeError) as ctx:\n        await app.cleanup()\n    assert \"has more than one 'yield'\" in str(ctx.value)\n    assert out == [\"pre_1\", \"post_1\"]\n\n\nasync def test_subapp_chained_config_dict_visibility(aiohttp_client) -> None:\n    key1 = web.AppKey(\"key1\", str)\n    key2 = web.AppKey(\"key2\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert key2 not in request.config_dict\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key1] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert request.config_dict[key2] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key2] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_chained_config_dict_overriding(aiohttp_client) -> None:\n    key = web.AppKey(\"key\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val1\"\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_on_startup(aiohttp_client) -> None:\n    subapp = web.Application()\n    startup = web.AppKey(\"startup\", bool)\n    cleanup = web.AppKey(\"cleanup\", bool)\n\n    startup_called = False\n\n    async def on_startup(app: web.Application) -> None:\n        nonlocal startup_called\n        startup_called = True\n        app[startup] = True\n\n    subapp.on_startup.append(on_startup)\n\n    ctx_pre_called = False\n    ctx_post_called = False\n\n    async def cleanup_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_pre_called, ctx_post_called\n        ctx_pre_called = True\n        app[cleanup] = True\n        yield None\n        ctx_post_called = True\n\n    subapp.cleanup_ctx.append(cleanup_ctx)\n\n    shutdown_called = False\n\n    async def on_shutdown(app: web.Application) -> None:\n        nonlocal shutdown_called\n        shutdown_called = True\n\n    subapp.on_shutdown.append(on_shutdown)\n\n    cleanup_called = False\n\n    async def on_cleanup(app: web.Application) -> None:\n        nonlocal cleanup_called\n        cleanup_called = True\n\n    subapp.on_cleanup.append(on_cleanup)\n\n    app = web.Application()\n\n    app.add_subapp(\"/subapp\", subapp)\n\n    assert not startup_called\n    assert not ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    assert subapp.on_startup.frozen\n    assert subapp.cleanup_ctx.frozen\n    assert subapp.on_shutdown.frozen\n    assert subapp.on_cleanup.frozen\n    assert subapp.router.frozen\n\n    client = await aiohttp_client(app)\n\n    assert startup_called\n    assert ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    await client.close()\n\n    assert startup_called\n    assert ctx_pre_called\n    assert ctx_post_called\n    assert shutdown_called\n    assert cleanup_called\n\n\n@pytest.mark.filterwarnings(r\"ignore:.*web\\.AppKey:UserWarning\")\ndef test_app_iter():\n    app = web.Application()\n    b = web.AppKey(\"b\", str)\n    c = web.AppKey(\"c\", str)\n    app[\"a\"] = \"0\"\n    app[b] = \"1\"\n    app[c] = \"2\"\n    app[\"d\"] = \"4\"\n    assert sorted(list(app)) == [b, c, \"a\", \"d\"]\n\n\ndef test_app_boolean() -> None:\n    app = web.Application()\n    assert app\n",
            "new_file_content": "import asyncio\nimport gc\nfrom typing import AsyncIterator, Callable, Iterator, NoReturn, Type\nfrom unittest import mock\n\nimport pytest\n\nfrom aiohttp import log, web\nfrom aiohttp.abc import AbstractAccessLogger, AbstractRouter\nfrom aiohttp.helpers import DEBUG\nfrom aiohttp.test_utils import make_mocked_coro\nfrom aiohttp.typedefs import Handler\n\n\nasync def test_app_ctor() -> None:\n    loop = asyncio.get_event_loop()\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(loop=loop)\n    with pytest.warns(DeprecationWarning):\n        assert loop is app.loop\n    assert app.logger is log.web_logger\n\n\ndef test_app_call() -> None:\n    app = web.Application()\n    assert app is app()\n\n\ndef test_app_default_loop() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is None\n\n\nasync def test_set_loop() -> None:\n    loop = asyncio.get_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n\ndef test_set_loop_default_loop() -> None:\n    loop = asyncio.new_event_loop()\n    asyncio.set_event_loop(loop)\n    app = web.Application()\n    try:\n        app._set_loop(None)\n        with pytest.warns(DeprecationWarning):\n            assert app.loop is loop\n        asyncio.set_event_loop(None)\n\n    finally:\n        # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n        loop.stop()\n        loop.run_forever()\n        loop.close()\n        gc.collect()\n\n\ndef test_set_loop_with_different_loops() -> None:\n    loop = asyncio.new_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n    with pytest.raises(RuntimeError):\n        app._set_loop(loop=object())\n\n    # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n    loop.stop()\n    loop.run_forever()\n    loop.close()\n    gc.collect()\n\n\n@pytest.mark.parametrize(\"debug\", [True, False])\nasync def test_app_make_handler_debug_exc(mocker, debug) -> None:\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(debug=debug)\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    with pytest.warns(DeprecationWarning):\n        assert app.debug == debug\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=debug,\n    )\n\n\nasync def test_app_make_handler_args(mocker) -> None:\n    app = web.Application(handler_args={\"test\": True})\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n        test=True,\n    )\n\n\nasync def test_app_make_handler_access_log_class(mocker) -> None:\n    class Logger:\n        pass\n\n    app = web.Application()\n\n    with pytest.raises(TypeError):\n        app._make_handler(access_log_class=Logger)\n\n    class Logger(AbstractAccessLogger):\n        def log(self, request, response, time):\n            self.logger.info(\"msg\")\n\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n    app = web.Application(handler_args={\"access_log_class\": Logger})\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n\nasync def test_app_make_handler_raises_deprecation_warning() -> None:\n    app = web.Application()\n\n    with pytest.warns(DeprecationWarning):\n        app.make_handler()\n\n\nasync def test_app_register_on_finish() -> None:\n    app = web.Application()\n    cb1 = make_mocked_coro(None)\n    cb2 = make_mocked_coro(None)\n    app.on_cleanup.append(cb1)\n    app.on_cleanup.append(cb2)\n    app.freeze()\n    await app.cleanup()\n    cb1.assert_called_once_with(app)\n    cb2.assert_called_once_with(app)\n\n\nasync def test_app_register_coro() -> None:\n    app = web.Application()\n    fut = asyncio.get_event_loop().create_future()\n\n    async def cb(app: web.Application) -> None:\n        await asyncio.sleep(0.001)\n        fut.set_result(123)\n\n    app.on_cleanup.append(cb)\n    app.freeze()\n    await app.cleanup()\n    assert fut.done()\n    assert 123 == fut.result()\n\n\ndef test_non_default_router() -> None:\n    router = mock.Mock(spec=AbstractRouter)\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(router=router)\n    assert router is app.router\n\n\ndef test_logging() -> None:\n    logger = mock.Mock()\n    app = web.Application()\n    app.logger = logger\n    assert app.logger is logger\n\n\nasync def test_on_shutdown() -> None:\n    app = web.Application()\n    called = False\n\n    async def on_shutdown(app_param: web.Application) -> None:\n        nonlocal called\n        assert app is app_param\n        called = True\n\n    app.on_shutdown.append(on_shutdown)\n    app.freeze()\n    await app.shutdown()\n    assert called\n\n\nasync def test_on_startup() -> None:\n    app = web.Application()\n\n    long_running1_called = False\n    long_running2_called = False\n    all_long_running_called = False\n\n    async def long_running1(app_param: web.Application) -> None:\n        nonlocal long_running1_called\n        assert app is app_param\n        long_running1_called = True\n\n    async def long_running2(app_param: web.Application) -> None:\n        nonlocal long_running2_called\n        assert app is app_param\n        long_running2_called = True\n\n    async def on_startup_all_long_running(app_param: web.Application) -> None:\n        nonlocal all_long_running_called\n        assert app is app_param\n        all_long_running_called = True\n        await asyncio.gather(long_running1(app_param), long_running2(app_param))\n\n    app.on_startup.append(on_startup_all_long_running)\n    app.freeze()\n\n    await app.startup()\n    assert long_running1_called\n    assert long_running2_called\n    assert all_long_running_called\n\n\ndef test_appkey() -> None:\n    key = web.AppKey(\"key\", str)\n    app = web.Application()\n    app[key] = \"value\"\n    assert app[key] == \"value\"\n    assert len(app) == 1\n    del app[key]\n    assert len(app) == 0\n\n\ndef test_appkey_repr_concrete() -> None:\n    key = web.AppKey(\"key\", int)\n    assert repr(key) in (\n        \"<AppKey(__channelexec__.key, type=int)>\",  # pytest-xdist\n        \"<AppKey(__main__.key, type=int)>\",\n    )\n    key2 = web.AppKey(\"key\", web.Request)\n    assert repr(key2) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=aiohttp.web_request.Request)>\",\n        \"<AppKey(__main__.key, type=aiohttp.web_request.Request)>\",\n    )\n\n\ndef test_appkey_repr_nonconcrete() -> None:\n    key = web.AppKey(\"key\", Iterator[int])\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_appkey_repr_annotated() -> None:\n    key = web.AppKey[Iterator[int]](\"key\")\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_app_str_keys() -> None:\n    app = web.Application()\n    with pytest.warns(\n        UserWarning, match=r\"web_advanced\\.html#application-s-config\"\n    ) as checker:\n        app[\"key\"] = \"value\"\n        # Check that the error is emitted at the call site (stacklevel=2)\n        assert checker[0].filename == __file__\n    assert app[\"key\"] == \"value\"\n\n\ndef test_app_get() -> None:\n    key = web.AppKey(\"key\", int)\n    app = web.Application()\n    assert app.get(key, \"foo\") == \"foo\"\n    app[key] = 5\n    assert app.get(key, \"foo\") == 5\n\n\ndef test_app_freeze() -> None:\n    app = web.Application()\n    subapp = mock.Mock()\n    subapp._middlewares = ()\n    app._subapps.append(subapp)\n\n    app.freeze()\n    assert subapp.freeze.called\n\n    app.freeze()\n    assert len(subapp.freeze.call_args_list) == 1\n\n\ndef test_equality() -> None:\n    app1 = web.Application()\n    app2 = web.Application()\n\n    assert app1 == app1\n    assert app1 != app2\n\n\ndef test_app_run_middlewares() -> None:\n\n    root = web.Application()\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is False\n\n    @web.middleware\n    async def middleware(request: web.Request, handler: Handler) -> web.StreamResponse:\n        return await handler(request)  # pragma: no cover\n\n    root = web.Application(middlewares=[middleware])\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n    root = web.Application()\n    sub = web.Application(middlewares=[middleware])\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n\ndef test_subapp_pre_frozen_after_adding() -> None:\n    app = web.Application()\n    subapp = web.Application()\n\n    app.add_subapp(\"/prefix\", subapp)\n    assert subapp.pre_frozen\n    assert not subapp.frozen\n\n\ndef test_app_inheritance() -> None:\n    with pytest.warns(DeprecationWarning):\n\n        class A(web.Application):  # type: ignore[misc]\n            pass\n\n\n@pytest.mark.skipif(not DEBUG, reason=\"The check is applied in DEBUG mode only\")\ndef test_app_custom_attr() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        app.custom = None  # type: ignore[attr-defined]\n\n\nasync def test_cleanup_ctx() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_startup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            if fail:\n                raise exc\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    with pytest.raises(Exception) as ctx:\n        await app.startup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_cleanup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise exc\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(Exception) as ctx:\n        await app.cleanup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_cleanup_after_exception() -> None:\n    app = web.Application()\n    ctx_state = None\n\n    async def success_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_state\n        ctx_state = \"START\"\n        yield\n        ctx_state = \"CLEAN\"\n\n    async def fail_ctx(app: web.Application) -> AsyncIterator[NoReturn]:\n        raise Exception()\n        yield\n\n    app.cleanup_ctx.append(success_ctx)\n    app.cleanup_ctx.append(fail_ctx)\n    runner = web.AppRunner(app)\n    try:\n        with pytest.raises(Exception):\n            await runner.setup()\n    finally:\n        await runner.cleanup()\n\n    assert ctx_state == \"CLEAN\"\n\n\n@pytest.mark.parametrize(\"exc_cls\", (Exception, asyncio.CancelledError))\nasync def test_cleanup_ctx_exception_on_cleanup_multiple(\n    exc_cls: Type[BaseException],\n) -> None:\n    app = web.Application()\n    out = []\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise exc_cls(\"fail_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3, True))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(web.CleanupError) as ctx:\n        await app.cleanup()\n    exc = ctx.value\n    assert len(exc.exceptions) == 2\n    assert str(exc.exceptions[0]) == \"fail_3\"\n    assert str(exc.exceptions[1]) == \"fail_2\"\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_multiple_yields() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            yield None\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\"]\n    with pytest.raises(RuntimeError) as ctx:\n        await app.cleanup()\n    assert \"has more than one 'yield'\" in str(ctx.value)\n    assert out == [\"pre_1\", \"post_1\"]\n\n\nasync def test_subapp_chained_config_dict_visibility(aiohttp_client) -> None:\n    key1 = web.AppKey(\"key1\", str)\n    key2 = web.AppKey(\"key2\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert key2 not in request.config_dict\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key1] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert request.config_dict[key2] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key2] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_chained_config_dict_overriding(aiohttp_client) -> None:\n    key = web.AppKey(\"key\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val1\"\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_on_startup(aiohttp_client) -> None:\n    subapp = web.Application()\n    startup = web.AppKey(\"startup\", bool)\n    cleanup = web.AppKey(\"cleanup\", bool)\n\n    startup_called = False\n\n    async def on_startup(app: web.Application) -> None:\n        nonlocal startup_called\n        startup_called = True\n        app[startup] = True\n\n    subapp.on_startup.append(on_startup)\n\n    ctx_pre_called = False\n    ctx_post_called = False\n\n    async def cleanup_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_pre_called, ctx_post_called\n        ctx_pre_called = True\n        app[cleanup] = True\n        yield None\n        ctx_post_called = True\n\n    subapp.cleanup_ctx.append(cleanup_ctx)\n\n    shutdown_called = False\n\n    async def on_shutdown(app: web.Application) -> None:\n        nonlocal shutdown_called\n        shutdown_called = True\n\n    subapp.on_shutdown.append(on_shutdown)\n\n    cleanup_called = False\n\n    async def on_cleanup(app: web.Application) -> None:\n        nonlocal cleanup_called\n        cleanup_called = True\n\n    subapp.on_cleanup.append(on_cleanup)\n\n    app = web.Application()\n\n    app.add_subapp(\"/subapp\", subapp)\n\n    assert not startup_called\n    assert not ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    assert subapp.on_startup.frozen\n    assert subapp.cleanup_ctx.frozen\n    assert subapp.on_shutdown.frozen\n    assert subapp.on_cleanup.frozen\n    assert subapp.router.frozen\n\n    client = await aiohttp_client(app)\n\n    assert startup_called\n    assert ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    await client.close()\n\n    assert startup_called\n    assert ctx_pre_called\n    assert ctx_post_called\n    assert shutdown_called\n    assert cleanup_called\n\n\n@pytest.mark.filterwarnings(r\"ignore:.*web\\.AppKey:UserWarning\")\ndef test_app_iter():\n    app = web.Application()\n    b = web.AppKey(\"b\", str)\n    c = web.AppKey(\"c\", str)\n    app[\"a\"] = \"0\"\n    app[b] = \"1\"\n    app[c] = \"2\"\n    app[\"d\"] = \"4\"\n    assert sorted(list(app)) == [b, c, \"a\", \"d\"]\n\n\ndef test_app_boolean() -> None:\n    app = web.Application()\n    assert app\n",
            "header": {
                "file": {
                    "path": "tests/test_web_app.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "3d3aa247",
                "new_commit_hash": "6a86a345",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_web_app.py"
            },
            "plus_file": {
                "path": "b/tests/test_web_app.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1,
                            "length": 6
                        },
                        "new_range": {
                            "start": 1,
                            "length": 6
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "import asyncio",
                                "type": "context"
                            },
                            {
                                "content": "import gc",
                                "type": "context"
                            },
                            {
                                "content": "from typing import AsyncIterator, Callable, Iterator, NoReturn",
                                "type": "deleted"
                            },
                            {
                                "content": "from typing import AsyncIterator, Callable, Iterator, NoReturn, Type",
                                "type": "added"
                            },
                            {
                                "content": "from unittest import mock",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import pytest",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_web_app.py",
                            "type": "statement",
                            "name": "",
                            "content": "from typing import AsyncIterator, Callable, Iterator, NoReturn, Type",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 3,
                            "end_lineno": 3,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "tests/test_web_app.py",
                            "type": "statement",
                            "name": "",
                            "content": "from typing import AsyncIterator, Callable, Iterator, NoReturn",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 3,
                            "end_lineno": 3,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 476,
                            "length": 7
                        },
                        "new_range": {
                            "start": 476,
                            "length": 10
                        },
                        "section": "async def test_cleanup_ctx_cleanup_after_exception() -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert ctx_state == \"CLEAN\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:",
                                "type": "deleted"
                            },
                            {
                                "content": "@pytest.mark.parametrize(\"exc_cls\", (Exception, asyncio.CancelledError))",
                                "type": "added"
                            },
                            {
                                "content": "async def test_cleanup_ctx_exception_on_cleanup_multiple(",
                                "type": "added"
                            },
                            {
                                "content": "    exc_cls: Type[BaseException],",
                                "type": "added"
                            },
                            {
                                "content": ") -> None:",
                                "type": "added"
                            },
                            {
                                "content": "    app = web.Application()",
                                "type": "context"
                            },
                            {
                                "content": "    out = []",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_app.py",
                            "type": "function",
                            "name": "test_cleanup_ctx_exception_on_cleanup_multiple",
                            "content": "async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:\n    app = web.Application()\n    out = []\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise Exception(\"fail_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3, True))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(web.CleanupError) as ctx:\n        await app.cleanup()\n    exc = ctx.value\n    assert len(exc.exceptions) == 2\n    assert str(exc.exceptions[0]) == \"fail_3\"\n    assert str(exc.exceptions[1]) == \"fail_2\"\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]",
                            "ast_type_str": "AsyncFunctionDef",
                            "start_lineno": 479,
                            "end_lineno": 507,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 488,
                            "length": 7
                        },
                        "new_range": {
                            "start": 491,
                            "length": 7
                        },
                        "section": "async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            yield None",
                                "type": "context"
                            },
                            {
                                "content": "            out.append(\"post_\" + str(num))",
                                "type": "context"
                            },
                            {
                                "content": "            if fail:",
                                "type": "context"
                            },
                            {
                                "content": "                raise Exception(\"fail_\" + str(num))",
                                "type": "deleted"
                            },
                            {
                                "content": "                raise exc_cls(\"fail_\" + str(num))",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        return inner",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_app.py",
                            "type": "function",
                            "name": "test_cleanup_ctx_exception_on_cleanup_multiple",
                            "content": "async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None:\n    app = web.Application()\n    out = []\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise Exception(\"fail_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3, True))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(web.CleanupError) as ctx:\n        await app.cleanup()\n    exc = ctx.value\n    assert len(exc.exceptions) == 2\n    assert str(exc.exceptions[0]) == \"fail_3\"\n    assert str(exc.exceptions[1]) == \"fail_2\"\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]",
                            "ast_type_str": "AsyncFunctionDef",
                            "start_lineno": 479,
                            "end_lineno": 507,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "87342c791dfd4c916877ba3dffafb9345bb0491f^",
    "new_commit_hash": "87342c791dfd4c916877ba3dffafb9345bb0491f",
    "commit_message": "Fix CancelledError stopping other cleanup contexts completing (#8908) (#8973)\n\n(cherry picked from commit 032fb571f2c73605d855d4f17026b303c7441823)",
    "commit_date": "2024-09-01T20:59:35+01:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "87342c791dfd4c916877ba3dffafb9345bb0491f",
    "test_file_codes": [
        "import asyncio\nimport gc\nfrom typing import AsyncIterator, Callable, Iterator, NoReturn, Type\nfrom unittest import mock\n\nimport pytest\n\nfrom aiohttp import log, web\nfrom aiohttp.abc import AbstractAccessLogger, AbstractRouter\nfrom aiohttp.helpers import DEBUG\nfrom aiohttp.test_utils import make_mocked_coro\nfrom aiohttp.typedefs import Handler\n\n\nasync def test_app_ctor() -> None:\n    loop = asyncio.get_event_loop()\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(loop=loop)\n    with pytest.warns(DeprecationWarning):\n        assert loop is app.loop\n    assert app.logger is log.web_logger\n\n\ndef test_app_call() -> None:\n    app = web.Application()\n    assert app is app()\n\n\ndef test_app_default_loop() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is None\n\n\nasync def test_set_loop() -> None:\n    loop = asyncio.get_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n\ndef test_set_loop_default_loop() -> None:\n    loop = asyncio.new_event_loop()\n    asyncio.set_event_loop(loop)\n    app = web.Application()\n    try:\n        app._set_loop(None)\n        with pytest.warns(DeprecationWarning):\n            assert app.loop is loop\n        asyncio.set_event_loop(None)\n\n    finally:\n        # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n        loop.stop()\n        loop.run_forever()\n        loop.close()\n        gc.collect()\n\n\ndef test_set_loop_with_different_loops() -> None:\n    loop = asyncio.new_event_loop()\n    app = web.Application()\n    app._set_loop(loop)\n    with pytest.warns(DeprecationWarning):\n        assert app.loop is loop\n\n    with pytest.raises(RuntimeError):\n        app._set_loop(loop=object())\n\n    # Cleanup, leaks into `test_app_make_handler_debug_exc[True]` otherwise:\n    loop.stop()\n    loop.run_forever()\n    loop.close()\n    gc.collect()\n\n\n@pytest.mark.parametrize(\"debug\", [True, False])\nasync def test_app_make_handler_debug_exc(mocker, debug) -> None:\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(debug=debug)\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    with pytest.warns(DeprecationWarning):\n        assert app.debug == debug\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=debug,\n    )\n\n\nasync def test_app_make_handler_args(mocker) -> None:\n    app = web.Application(handler_args={\"test\": True})\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler()\n    srv.assert_called_with(\n        app._handle,\n        request_factory=app._make_request,\n        access_log_class=mock.ANY,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n        test=True,\n    )\n\n\nasync def test_app_make_handler_access_log_class(mocker) -> None:\n    class Logger:\n        pass\n\n    app = web.Application()\n\n    with pytest.raises(TypeError):\n        app._make_handler(access_log_class=Logger)\n\n    class Logger(AbstractAccessLogger):\n        def log(self, request, response, time):\n            self.logger.info(\"msg\")\n\n    srv = mocker.patch(\"aiohttp.web_app.Server\")\n\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n    app = web.Application(handler_args={\"access_log_class\": Logger})\n    app._make_handler(access_log_class=Logger)\n    srv.assert_called_with(\n        app._handle,\n        access_log_class=Logger,\n        request_factory=app._make_request,\n        loop=asyncio.get_event_loop(),\n        debug=mock.ANY,\n    )\n\n\nasync def test_app_make_handler_raises_deprecation_warning() -> None:\n    app = web.Application()\n\n    with pytest.warns(DeprecationWarning):\n        app.make_handler()\n\n\nasync def test_app_register_on_finish() -> None:\n    app = web.Application()\n    cb1 = make_mocked_coro(None)\n    cb2 = make_mocked_coro(None)\n    app.on_cleanup.append(cb1)\n    app.on_cleanup.append(cb2)\n    app.freeze()\n    await app.cleanup()\n    cb1.assert_called_once_with(app)\n    cb2.assert_called_once_with(app)\n\n\nasync def test_app_register_coro() -> None:\n    app = web.Application()\n    fut = asyncio.get_event_loop().create_future()\n\n    async def cb(app: web.Application) -> None:\n        await asyncio.sleep(0.001)\n        fut.set_result(123)\n\n    app.on_cleanup.append(cb)\n    app.freeze()\n    await app.cleanup()\n    assert fut.done()\n    assert 123 == fut.result()\n\n\ndef test_non_default_router() -> None:\n    router = mock.Mock(spec=AbstractRouter)\n    with pytest.warns(DeprecationWarning):\n        app = web.Application(router=router)\n    assert router is app.router\n\n\ndef test_logging() -> None:\n    logger = mock.Mock()\n    app = web.Application()\n    app.logger = logger\n    assert app.logger is logger\n\n\nasync def test_on_shutdown() -> None:\n    app = web.Application()\n    called = False\n\n    async def on_shutdown(app_param: web.Application) -> None:\n        nonlocal called\n        assert app is app_param\n        called = True\n\n    app.on_shutdown.append(on_shutdown)\n    app.freeze()\n    await app.shutdown()\n    assert called\n\n\nasync def test_on_startup() -> None:\n    app = web.Application()\n\n    long_running1_called = False\n    long_running2_called = False\n    all_long_running_called = False\n\n    async def long_running1(app_param: web.Application) -> None:\n        nonlocal long_running1_called\n        assert app is app_param\n        long_running1_called = True\n\n    async def long_running2(app_param: web.Application) -> None:\n        nonlocal long_running2_called\n        assert app is app_param\n        long_running2_called = True\n\n    async def on_startup_all_long_running(app_param: web.Application) -> None:\n        nonlocal all_long_running_called\n        assert app is app_param\n        all_long_running_called = True\n        await asyncio.gather(long_running1(app_param), long_running2(app_param))\n\n    app.on_startup.append(on_startup_all_long_running)\n    app.freeze()\n\n    await app.startup()\n    assert long_running1_called\n    assert long_running2_called\n    assert all_long_running_called\n\n\ndef test_appkey() -> None:\n    key = web.AppKey(\"key\", str)\n    app = web.Application()\n    app[key] = \"value\"\n    assert app[key] == \"value\"\n    assert len(app) == 1\n    del app[key]\n    assert len(app) == 0\n\n\ndef test_appkey_repr_concrete() -> None:\n    key = web.AppKey(\"key\", int)\n    assert repr(key) in (\n        \"<AppKey(__channelexec__.key, type=int)>\",  # pytest-xdist\n        \"<AppKey(__main__.key, type=int)>\",\n    )\n    key2 = web.AppKey(\"key\", web.Request)\n    assert repr(key2) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=aiohttp.web_request.Request)>\",\n        \"<AppKey(__main__.key, type=aiohttp.web_request.Request)>\",\n    )\n\n\ndef test_appkey_repr_nonconcrete() -> None:\n    key = web.AppKey(\"key\", Iterator[int])\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_appkey_repr_annotated() -> None:\n    key = web.AppKey[Iterator[int]](\"key\")\n    assert repr(key) in (\n        # pytest-xdist:\n        \"<AppKey(__channelexec__.key, type=typing.Iterator[int])>\",\n        \"<AppKey(__main__.key, type=typing.Iterator[int])>\",\n    )\n\n\ndef test_app_str_keys() -> None:\n    app = web.Application()\n    with pytest.warns(\n        UserWarning, match=r\"web_advanced\\.html#application-s-config\"\n    ) as checker:\n        app[\"key\"] = \"value\"\n        # Check that the error is emitted at the call site (stacklevel=2)\n        assert checker[0].filename == __file__\n    assert app[\"key\"] == \"value\"\n\n\ndef test_app_get() -> None:\n    key = web.AppKey(\"key\", int)\n    app = web.Application()\n    assert app.get(key, \"foo\") == \"foo\"\n    app[key] = 5\n    assert app.get(key, \"foo\") == 5\n\n\ndef test_app_freeze() -> None:\n    app = web.Application()\n    subapp = mock.Mock()\n    subapp._middlewares = ()\n    app._subapps.append(subapp)\n\n    app.freeze()\n    assert subapp.freeze.called\n\n    app.freeze()\n    assert len(subapp.freeze.call_args_list) == 1\n\n\ndef test_equality() -> None:\n    app1 = web.Application()\n    app2 = web.Application()\n\n    assert app1 == app1\n    assert app1 != app2\n\n\ndef test_app_run_middlewares() -> None:\n\n    root = web.Application()\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is False\n\n    @web.middleware\n    async def middleware(request: web.Request, handler: Handler) -> web.StreamResponse:\n        return await handler(request)  # pragma: no cover\n\n    root = web.Application(middlewares=[middleware])\n    sub = web.Application()\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n    root = web.Application()\n    sub = web.Application(middlewares=[middleware])\n    root.add_subapp(\"/sub\", sub)\n    root.freeze()\n    assert root._run_middlewares is True\n\n\ndef test_subapp_pre_frozen_after_adding() -> None:\n    app = web.Application()\n    subapp = web.Application()\n\n    app.add_subapp(\"/prefix\", subapp)\n    assert subapp.pre_frozen\n    assert not subapp.frozen\n\n\ndef test_app_inheritance() -> None:\n    with pytest.warns(DeprecationWarning):\n\n        class A(web.Application):  # type: ignore[misc]\n            pass\n\n\n@pytest.mark.skipif(not DEBUG, reason=\"The check is applied in DEBUG mode only\")\ndef test_app_custom_attr() -> None:\n    app = web.Application()\n    with pytest.warns(DeprecationWarning):\n        app.custom = None  # type: ignore[attr-defined]\n\n\nasync def test_cleanup_ctx() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_startup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            if fail:\n                raise exc\n            yield None\n            out.append(\"post_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    with pytest.raises(Exception) as ctx:\n        await app.startup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\"]\n    await app.cleanup()\n    assert out == [\"pre_1\", \"pre_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_exception_on_cleanup() -> None:\n    app = web.Application()\n    out = []\n\n    exc = Exception(\"fail\")\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise exc\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(Exception) as ctx:\n        await app.cleanup()\n    assert ctx.value is exc\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_cleanup_after_exception() -> None:\n    app = web.Application()\n    ctx_state = None\n\n    async def success_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_state\n        ctx_state = \"START\"\n        yield\n        ctx_state = \"CLEAN\"\n\n    async def fail_ctx(app: web.Application) -> AsyncIterator[NoReturn]:\n        raise Exception()\n        yield\n\n    app.cleanup_ctx.append(success_ctx)\n    app.cleanup_ctx.append(fail_ctx)\n    runner = web.AppRunner(app)\n    try:\n        with pytest.raises(Exception):\n            await runner.setup()\n    finally:\n        await runner.cleanup()\n\n    assert ctx_state == \"CLEAN\"\n\n\n@pytest.mark.parametrize(\"exc_cls\", (Exception, asyncio.CancelledError))\nasync def test_cleanup_ctx_exception_on_cleanup_multiple(\n    exc_cls: Type[BaseException],\n) -> None:\n    app = web.Application()\n    out = []\n\n    def f(\n        num: int, fail: bool = False\n    ) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            if fail:\n                raise exc_cls(\"fail_\" + str(num))\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.cleanup_ctx.append(f(2, True))\n    app.cleanup_ctx.append(f(3, True))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n    with pytest.raises(web.CleanupError) as ctx:\n        await app.cleanup()\n    exc = ctx.value\n    assert len(exc.exceptions) == 2\n    assert str(exc.exceptions[0]) == \"fail_3\"\n    assert str(exc.exceptions[1]) == \"fail_2\"\n    assert out == [\"pre_1\", \"pre_2\", \"pre_3\", \"post_3\", \"post_2\", \"post_1\"]\n\n\nasync def test_cleanup_ctx_multiple_yields() -> None:\n    app = web.Application()\n    out = []\n\n    def f(num: int) -> Callable[[web.Application], AsyncIterator[None]]:\n        async def inner(app: web.Application) -> AsyncIterator[None]:\n            out.append(\"pre_\" + str(num))\n            yield None\n            out.append(\"post_\" + str(num))\n            yield None\n\n        return inner\n\n    app.cleanup_ctx.append(f(1))\n    app.freeze()\n    await app.startup()\n    assert out == [\"pre_1\"]\n    with pytest.raises(RuntimeError) as ctx:\n        await app.cleanup()\n    assert \"has more than one 'yield'\" in str(ctx.value)\n    assert out == [\"pre_1\", \"post_1\"]\n\n\nasync def test_subapp_chained_config_dict_visibility(aiohttp_client) -> None:\n    key1 = web.AppKey(\"key1\", str)\n    key2 = web.AppKey(\"key2\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert key2 not in request.config_dict\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key1] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key1] == \"val1\"\n        assert request.config_dict[key2] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key2] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_chained_config_dict_overriding(aiohttp_client) -> None:\n    key = web.AppKey(\"key\", str)\n\n    async def main_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val1\"\n        return web.Response(status=200)\n\n    root = web.Application()\n    root[key] = \"val1\"\n    root.add_routes([web.get(\"/\", main_handler)])\n\n    async def sub_handler(request: web.Request) -> web.Response:\n        assert request.config_dict[key] == \"val2\"\n        return web.Response(status=201)\n\n    sub = web.Application()\n    sub[key] = \"val2\"\n    sub.add_routes([web.get(\"/\", sub_handler)])\n    root.add_subapp(\"/sub\", sub)\n\n    client = await aiohttp_client(root)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 200\n    resp = await client.get(\"/sub/\")\n    assert resp.status == 201\n\n\nasync def test_subapp_on_startup(aiohttp_client) -> None:\n    subapp = web.Application()\n    startup = web.AppKey(\"startup\", bool)\n    cleanup = web.AppKey(\"cleanup\", bool)\n\n    startup_called = False\n\n    async def on_startup(app: web.Application) -> None:\n        nonlocal startup_called\n        startup_called = True\n        app[startup] = True\n\n    subapp.on_startup.append(on_startup)\n\n    ctx_pre_called = False\n    ctx_post_called = False\n\n    async def cleanup_ctx(app: web.Application) -> AsyncIterator[None]:\n        nonlocal ctx_pre_called, ctx_post_called\n        ctx_pre_called = True\n        app[cleanup] = True\n        yield None\n        ctx_post_called = True\n\n    subapp.cleanup_ctx.append(cleanup_ctx)\n\n    shutdown_called = False\n\n    async def on_shutdown(app: web.Application) -> None:\n        nonlocal shutdown_called\n        shutdown_called = True\n\n    subapp.on_shutdown.append(on_shutdown)\n\n    cleanup_called = False\n\n    async def on_cleanup(app: web.Application) -> None:\n        nonlocal cleanup_called\n        cleanup_called = True\n\n    subapp.on_cleanup.append(on_cleanup)\n\n    app = web.Application()\n\n    app.add_subapp(\"/subapp\", subapp)\n\n    assert not startup_called\n    assert not ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    assert subapp.on_startup.frozen\n    assert subapp.cleanup_ctx.frozen\n    assert subapp.on_shutdown.frozen\n    assert subapp.on_cleanup.frozen\n    assert subapp.router.frozen\n\n    client = await aiohttp_client(app)\n\n    assert startup_called\n    assert ctx_pre_called\n    assert not ctx_post_called\n    assert not shutdown_called\n    assert not cleanup_called\n\n    await client.close()\n\n    assert startup_called\n    assert ctx_pre_called\n    assert ctx_post_called\n    assert shutdown_called\n    assert cleanup_called\n\n\n@pytest.mark.filterwarnings(r\"ignore:.*web\\.AppKey:UserWarning\")\ndef test_app_iter():\n    app = web.Application()\n    b = web.AppKey(\"b\", str)\n    c = web.AppKey(\"c\", str)\n    app[\"a\"] = \"0\"\n    app[b] = \"1\"\n    app[c] = \"2\"\n    app[\"d\"] = \"4\"\n    assert sorted(list(app)) == [b, c, \"a\", \"d\"]\n\n\ndef test_app_boolean() -> None:\n    app = web.Application()\n    assert app\n",
        "import asyncio\nimport base64\nimport os\nimport socket\nimport ssl\nimport sys\nfrom hashlib import md5, sha1, sha256\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import Any\nfrom unittest import mock\nfrom uuid import uuid4\n\nimport pytest\n\nfrom aiohttp.http import WS_KEY\nfrom aiohttp.test_utils import loop_context\n\ntry:\n    import trustme\n\n    # Check if the CA is available in runtime, MacOS on Py3.10 fails somehow\n    trustme.CA()\n\n    TRUSTME: bool = True\nexcept ImportError:\n    TRUSTME = False\n\npytest_plugins = [\"aiohttp.pytest_plugin\", \"pytester\"]\n\nIS_HPUX = sys.platform.startswith(\"hp-ux\")\nIS_LINUX = sys.platform.startswith(\"linux\")\n\n\n@pytest.fixture\ndef tls_certificate_authority():\n    if not TRUSTME:\n        pytest.xfail(\"trustme is not supported\")\n    return trustme.CA()\n\n\n@pytest.fixture\ndef tls_certificate(tls_certificate_authority):\n    return tls_certificate_authority.issue_cert(\n        \"localhost\",\n        \"xn--prklad-4va.localhost\",\n        \"127.0.0.1\",\n        \"::1\",\n    )\n\n\n@pytest.fixture\ndef ssl_ctx(tls_certificate):\n    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)\n    tls_certificate.configure_cert(ssl_ctx)\n    return ssl_ctx\n\n\n@pytest.fixture\ndef client_ssl_ctx(tls_certificate_authority):\n    ssl_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)\n    tls_certificate_authority.configure_trust(ssl_ctx)\n    return ssl_ctx\n\n\n@pytest.fixture\ndef tls_ca_certificate_pem_path(tls_certificate_authority):\n    with tls_certificate_authority.cert_pem.tempfile() as ca_cert_pem:\n        yield ca_cert_pem\n\n\n@pytest.fixture\ndef tls_certificate_pem_path(tls_certificate):\n    with tls_certificate.private_key_and_cert_chain_pem.tempfile() as cert_pem:\n        yield cert_pem\n\n\n@pytest.fixture\ndef tls_certificate_pem_bytes(tls_certificate):\n    return tls_certificate.cert_chain_pems[0].bytes()\n\n\n@pytest.fixture\ndef tls_certificate_fingerprint_sha256(tls_certificate_pem_bytes):\n    tls_cert_der = ssl.PEM_cert_to_DER_cert(tls_certificate_pem_bytes.decode())\n    return sha256(tls_cert_der).digest()\n\n\n@pytest.fixture\ndef unix_sockname(tmp_path, tmp_path_factory):\n    \"\"\"Generate an fs path to the UNIX domain socket for testing.\n\n    N.B. Different OS kernels have different fs path length limitations\n    for it. For Linux, it's 108, for HP-UX it's 92 (or higher) depending\n    on its version. For for most of the BSDs (Open, Free, macOS) it's\n    mostly 104 but sometimes it can be down to 100.\n\n    Ref: https://github.com/aio-libs/aiohttp/issues/3572\n    \"\"\"\n    if not hasattr(socket, \"AF_UNIX\"):\n        pytest.skip(\"requires UNIX sockets\")\n\n    max_sock_len = 92 if IS_HPUX else 108 if IS_LINUX else 100\n    \"\"\"Amount of bytes allocated for the UNIX socket path by OS kernel.\n\n    Ref: https://unix.stackexchange.com/a/367012/27133\n    \"\"\"\n\n    sock_file_name = \"unix.sock\"\n    unique_prefix = f\"{uuid4()!s}-\"\n    unique_prefix_len = len(unique_prefix.encode())\n\n    root_tmp_dir = Path(\"/tmp\").resolve()\n    os_tmp_dir = Path(os.getenv(\"TMPDIR\", \"/tmp\")).resolve()\n    original_base_tmp_path = Path(\n        str(tmp_path_factory.getbasetemp()),\n    ).resolve()\n\n    original_base_tmp_path_hash = md5(\n        str(original_base_tmp_path).encode(),\n    ).hexdigest()\n\n    def make_tmp_dir(base_tmp_dir):\n        return TemporaryDirectory(\n            dir=str(base_tmp_dir),\n            prefix=\"pt-\",\n            suffix=f\"-{original_base_tmp_path_hash!s}\",\n        )\n\n    def assert_sock_fits(sock_path):\n        sock_path_len = len(sock_path.encode())\n        # exit-check to verify that it's correct and simplify debugging\n        # in the future\n        assert sock_path_len <= max_sock_len, (\n            \"Suggested UNIX socket ({sock_path}) is {sock_path_len} bytes \"\n            \"long but the current kernel only has {max_sock_len} bytes \"\n            \"allocated to hold it so it must be shorter. \"\n            \"See https://github.com/aio-libs/aiohttp/issues/3572 \"\n            \"for more info.\"\n        ).format_map(locals())\n\n    paths = original_base_tmp_path, os_tmp_dir, root_tmp_dir\n    unique_paths = [p for n, p in enumerate(paths) if p not in paths[:n]]\n    paths_num = len(unique_paths)\n\n    for num, tmp_dir_path in enumerate(paths, 1):\n        with make_tmp_dir(tmp_dir_path) as tmpd:\n            tmpd = Path(tmpd).resolve()\n            sock_path = str(tmpd / sock_file_name)\n            sock_path_len = len(sock_path.encode())\n\n            if num >= paths_num:\n                # exit-check to verify that it's correct and simplify\n                # debugging in the future\n                assert_sock_fits(sock_path)\n\n            if sock_path_len <= max_sock_len:\n                if max_sock_len - sock_path_len >= unique_prefix_len:\n                    # If we're lucky to have extra space in the path,\n                    # let's also make it more unique\n                    sock_path = str(tmpd / \"\".join((unique_prefix, sock_file_name)))\n                    # Double-checking it:\n                    assert_sock_fits(sock_path)\n                yield sock_path\n                return\n\n\n@pytest.fixture\ndef pipe_name():\n    name = rf\"\\\\.\\pipe\\{uuid4().hex}\"\n    return name\n\n\n@pytest.fixture\ndef create_mocked_conn(loop: Any):\n    def _proto_factory(conn_closing_result=None, **kwargs):\n        proto = mock.Mock(**kwargs)\n        proto.closed = loop.create_future()\n        proto.closed.set_result(conn_closing_result)\n        return proto\n\n    yield _proto_factory\n\n\n@pytest.fixture\ndef selector_loop():\n    policy = asyncio.WindowsSelectorEventLoopPolicy()\n    asyncio.set_event_loop_policy(policy)\n\n    with loop_context(policy.new_event_loop) as _loop:\n        asyncio.set_event_loop(_loop)\n        yield _loop\n\n\n@pytest.fixture\ndef netrc_contents(\n    tmp_path: Path,\n    monkeypatch: pytest.MonkeyPatch,\n    request: pytest.FixtureRequest,\n):\n    \"\"\"\n    Prepare :file:`.netrc` with given contents.\n\n    Monkey-patches :envvar:`NETRC` to point to created file.\n    \"\"\"\n    netrc_contents = getattr(request, \"param\", None)\n\n    netrc_file_path = tmp_path / \".netrc\"\n    if netrc_contents is not None:\n        netrc_file_path.write_text(netrc_contents)\n\n    monkeypatch.setenv(\"NETRC\", str(netrc_file_path))\n\n    return netrc_file_path\n\n\n@pytest.fixture\ndef start_connection():\n    with mock.patch(\n        \"aiohttp.connector.aiohappyeyeballs.start_connection\",\n        autospec=True,\n        spec_set=True,\n    ) as start_connection_mock:\n        yield start_connection_mock\n\n\n@pytest.fixture\ndef key_data():\n    return os.urandom(16)\n\n\n@pytest.fixture\ndef key(key_data: Any):\n    return base64.b64encode(key_data)\n\n\n@pytest.fixture\ndef ws_key(key: Any):\n    return base64.b64encode(sha1(key + WS_KEY).digest()).decode()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "re-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/requirements/cython.txt\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/hdrs.py\n./tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_helpers.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_http_parser.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_http_writer.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_websocket.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_helpers.pyi\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_cparser.pxd\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/_find_header.pxd\ncython -3 -o aiohttp/_helpers.c aiohttp/_helpers.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_parser.c aiohttp/_http_parser.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_writer.c aiohttp/_http_writer.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_websocket.c aiohttp/_websocket.pyx -I aiohttp -Werror\ncd vendor/llhttp; npm ci\n\n> llhttp@9.2.1 prepare\n> npm run clean && npm run build-ts\n\n\n> llhttp@9.2.1 clean\n> rm -rf lib && rm -rf test/tmp\n\n\n> llhttp@9.2.1 build-ts\n> tsc\n\n\nadded 213 packages, and audited 214 packages in 6s\n\n41 packages are looking for funding\n  run `npm fund` for details\n\n5 vulnerabilities (3 moderate, 2 high)\n\nTo address issues that do not require attention, run:\n  npm audit fix\n\nSome issues need review, and may require choosing\na different dependency.\n\nRun `npm audit` for details.\nmake -C vendor/llhttp generate\nmake[1]: Entering directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/vendor/llhttp'\nnpx ts-node bin/generate.ts\nmake[1]: Leaving directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/vendor/llhttp'\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/__init__.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/abc.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/base_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/client.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/client_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/client_proto.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/client_reqrep.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/client_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/compression_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/connector.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/cookiejar.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/formdata.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/http.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/http_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/http_parser.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/http_websocket.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/http_writer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/locks.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/multipart.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/payload.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/payload_streamer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/pytest_plugin.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/resolver.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/streams.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/tcp_helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/test_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/tracing.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/typedefs.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_app.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_fileresponse.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_middlewares.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_request.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_response.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_routedef.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_runner.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_server.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_urldispatcher.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/web_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/worker.py\nuv pip install -e . -c requirements/runtime-deps.txt\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 2ms\nPrepared 1 package in 0.34ms\nInstalled 1 package in 24ms\n + pip==24.3.1\nResolved 2 packages in 14ms\nInstalled 2 packages in 68ms\n + cython==3.0.10\n + multidict==6.0.5\nResolved 96 packages in 48ms\nUninstalled 1 package in 8ms\nInstalled 95 packages in 673ms\n + aiodns==3.2.0\n + aiohappyeyeballs==2.3.4\n + aiohttp-theme==0.1.6\n + aioredis==2.0.1\n + aiosignal==1.3.1\n + alabaster==0.7.13\n + annotated-types==0.5.0\n + async-timeout==4.0.3\n + attrs==23.2.0\n + babel==2.12.1\n + blockdiag==3.0.0\n + brotli==1.1.0\n + build==1.0.3\n + certifi==2023.7.22\n + cffi==1.17.0\n + cfgv==3.3.1\n + charset-normalizer==3.2.0\n + cherry-picker==2.2.0\n + click==8.1.6\n + coverage==7.6.0\n + cryptography==41.0.3\n + distlib==0.3.7\n + docutils==0.20.1\n + exceptiongroup==1.1.2\n + filelock==3.12.2\n + freezegun==1.5.1\n + frozenlist==1.4.1\n + funcparserlib==1.0.1\n + gidgethub==5.3.0\n + gunicorn==22.0.0\n + identify==2.5.26\n + idna==3.4\n + imagesize==1.4.1\n + importlib-metadata==7.0.0\n + importlib-resources==6.1.1\n + incremental==22.10.0\n + iniconfig==2.0.0\n + jinja2==3.1.2\n + markupsafe==2.1.3\n + mypy==1.11.1\n + mypy-extensions==1.0.0\n + nodeenv==1.8.0\n + packaging==23.1\n + pillow==9.5.0\n - pip==24.3.1\n + pip==23.2.1\n + pip-tools==7.4.1\n + platformdirs==3.10.0\n + pluggy==1.5.0\n + pre-commit==3.5.0\n + proxy-py==2.4.4\n + pycares==4.3.0\n + pycparser==2.21\n + pydantic==2.2.0\n + pydantic-core==2.6.0\n + pygments==2.15.1\n + pyjwt==2.8.0\n + pyproject-hooks==1.0.0\n + pytest==8.3.2\n + pytest-cov==5.0.0\n + pytest-mock==3.14.0\n + python-dateutil==2.8.2\n + python-on-whales==0.72.0\n + pyyaml==6.0.1\n + re-assert==1.1.0\n + regex==2023.6.3\n + requests==2.31.0\n + setuptools==68.0.0\n + setuptools-git==1.2\n + six==1.16.0\n + slotscheck==0.19.0\n + snowballstemmer==2.2.0\n + sphinx==7.1.2\n + sphinxcontrib-applehelp==1.0.4\n + sphinxcontrib-blockdiag==3.0.0\n + sphinxcontrib-devhelp==1.0.2\n + sphinxcontrib-htmlhelp==2.0.1\n + sphinxcontrib-jsmath==1.0.1\n + sphinxcontrib-qthelp==1.0.3\n + sphinxcontrib-serializinghtml==1.1.5\n + sphinxcontrib-towncrier==0.4.0a0\n + tomli==2.0.1\n + towncrier==23.11.0\n + tqdm==4.65.0\n + trustme==1.1.0\n + typer==0.9.0\n + typing-extensions==4.11.0\n + uritemplate==4.1.1\n + urllib3==2.0.4\n + uvloop==0.19.0\n + virtualenv==20.24.2\n + wait-for-it==2.2.2\n + webcolors==1.13\n + wheel==0.41.0\n + yarl==1.9.6\n + zipp==3.17.0\nwarning: `yarl==1.9.6` is yanked (reason: \"Performance regression: https://github.com/aio-libs/yarl/pull/1198\")\nResolved 9 packages in 1.72s\nPrepared 1 package in 17.53s\nInstalled 1 package in 0.64ms\n + aiohttp==3.10.6.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f)\nResolved 24 packages in 16ms\nInstalled 5 packages in 5ms\n + async-generator==1.10\n + brotlipy==0.7.0\n + chardet==5.2.0\n + multdict==0.0.5\n + pytest-asyncio==0.25.0\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.2, pluggy-1.5.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f\nconfigfile: setup.cfg\nplugins: cov-5.0.0, asyncio-0.25.0, mock-3.14.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollecting ... collected 41 items\n\nr2e_tests/test_1.py::test_app_ctor PASSED                                [  2%]\nr2e_tests/test_1.py::test_app_call PASSED                                [  4%]\nr2e_tests/test_1.py::test_app_default_loop PASSED                        [  7%]\nr2e_tests/test_1.py::test_set_loop PASSED                                [  9%]\nr2e_tests/test_1.py::test_set_loop_default_loop PASSED                   [ 12%]\nr2e_tests/test_1.py::test_set_loop_with_different_loops PASSED           [ 14%]\nr2e_tests/test_1.py::test_app_make_handler_debug_exc[True] PASSED        [ 17%]\nr2e_tests/test_1.py::test_app_make_handler_debug_exc[False] PASSED       [ 19%]\nr2e_tests/test_1.py::test_app_make_handler_args PASSED                   [ 21%]\nr2e_tests/test_1.py::test_app_make_handler_access_log_class PASSED       [ 24%]\nr2e_tests/test_1.py::test_app_make_handler_raises_deprecation_warning PASSED [ 26%]\nr2e_tests/test_1.py::test_app_register_on_finish PASSED                  [ 29%]\nr2e_tests/test_1.py::test_app_register_coro PASSED                       [ 31%]\nr2e_tests/test_1.py::test_non_default_router PASSED                      [ 34%]\nr2e_tests/test_1.py::test_logging PASSED                                 [ 36%]\nr2e_tests/test_1.py::test_on_shutdown PASSED                             [ 39%]\nr2e_tests/test_1.py::test_on_startup PASSED                              [ 41%]\nr2e_tests/test_1.py::test_appkey PASSED                                  [ 43%]\nr2e_tests/test_1.py::test_appkey_repr_concrete PASSED                    [ 46%]\nr2e_tests/test_1.py::test_appkey_repr_nonconcrete PASSED                 [ 48%]\nr2e_tests/test_1.py::test_appkey_repr_annotated PASSED                   [ 51%]\nr2e_tests/test_1.py::test_app_str_keys PASSED                            [ 53%]\nr2e_tests/test_1.py::test_app_get PASSED                                 [ 56%]\nr2e_tests/test_1.py::test_app_freeze PASSED                              [ 58%]\nr2e_tests/test_1.py::test_equality PASSED                                [ 60%]\nr2e_tests/test_1.py::test_app_run_middlewares PASSED                     [ 63%]\nr2e_tests/test_1.py::test_subapp_pre_frozen_after_adding PASSED          [ 65%]\nr2e_tests/test_1.py::test_app_inheritance PASSED                         [ 68%]\nr2e_tests/test_1.py::test_app_custom_attr SKIPPED (The check is appl...) [ 70%]\nr2e_tests/test_1.py::test_cleanup_ctx PASSED                             [ 73%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_startup PASSED        [ 75%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup PASSED        [ 78%]\nr2e_tests/test_1.py::test_cleanup_ctx_cleanup_after_exception PASSED     [ 80%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[Exception] PASSED [ 82%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError] PASSED [ 85%]\nr2e_tests/test_1.py::test_cleanup_ctx_multiple_yields PASSED             [ 87%]\nr2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop] PASSED [ 90%]\nr2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop] PASSED [ 92%]\nr2e_tests/test_1.py::test_subapp_on_startup[pyloop] PASSED               [ 95%]\nr2e_tests/test_1.py::test_app_iter PASSED                                [ 97%]\nr2e_tests/test_1.py::test_app_boolean PASSED                             [100%]\n\n==================================== PASSES ====================================\n\n---------- coverage: platform linux, python 3.9.21-final-0 -----------\nName                                     Stmts   Miss Branch BrPart  Cover\n--------------------------------------------------------------------------\naiohttp/__init__.py                         27     10      2      0    59%\naiohttp/abc.py                              97      2      0      0    98%\naiohttp/base_protocol.py                    67     36     22      3    38%\naiohttp/client.py                          545    264    196     51    45%\naiohttp/client_exceptions.py               154     65     20      1    52%\naiohttp/client_proto.py                    165     54     56     19    60%\naiohttp/client_reqrep.py                   695    310    300     73    47%\naiohttp/client_ws.py                       259    212     80      1    14%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       770    441    319     49    36%\naiohttp/cookiejar.py                       255    174    118      9    24%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         501    223    172     24    49%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     504    404    208      1    14%\naiohttp/http_websocket.py                  390    285    154      0    20%\naiohttp/http_writer.py                     118     44     50     13    53%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       623    508    266      1    13%\naiohttp/payload.py                         237    113     58      1    44%\naiohttp/payload_streamer.py                 31     11      4      0    69%\naiohttp/pytest_plugin.py                   162     61     42      6    60%\naiohttp/resolver.py                         80     55     26      2    25%\naiohttp/streams.py                         398    250    146     11    29%\naiohttp/tcp_helpers.py                      19      2      6      3    80%\naiohttp/test_utils.py                      315    115     50     14    60%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             118     81     52      0    23%\naiohttp/web_app.py                         282     23     74     10    88%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     37     20      1    59%\naiohttp/web_middlewares.py                  55     37     20      0    24%\naiohttp/web_protocol.py                    354    132    155     48    53%\naiohttp/web_request.py                     441    241    134      8    37%\naiohttp/web_response.py                    479    258    220     33    36%\naiohttp/web_routedef.py                    100     39     10      1    60%\naiohttp/web_runner.py                      213     72     36     10    62%\naiohttp/web_server.py                       42      6     12      2    78%\naiohttp/web_urldispatcher.py               742    341    161     23    49%\naiohttp/web_ws.py                          382    310    132      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          130    130     16      0     0%\ntests/test_base_protocol.py                198    198      2      0     0%\ntests/test_circular_imports.py              29     29     13      0     0%\ntests/test_classbasedview.py                39     39      0      0     0%\ntests/test_client_connection.py             94     94      2      0     0%\ntests/test_client_exceptions.py            199    199     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2455   2455     86      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               808    808     18      0     0%\ntests/test_client_response.py              466    466      4      0     0%\ntests/test_client_session.py               556    556     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         750    750     32      0     0%\ntests/test_connector.py                   1676   1676     82      0     0%\ntests/test_cookiejar.py                    350    350     24      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      81     81      6      0     0%\ntests/test_helpers.py                      470    470     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1088   1088     66      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    826    826     32      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       95     95      0      0     0%\ntests/test_proxy.py                        301    301      0      0     0%\ntests/test_proxy_functional.py             459    459     18      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     193    193     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      554    554     30      0     0%\ntests/test_streams.py                     1076   1076     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   244    244      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  851    851     24      0     0%\ntests/test_web_app.py                      454    454      6      0     0%\ntests/test_web_cli.py                       75     75      0      0     0%\ntests/test_web_exceptions.py               176    176     22      0     0%\ntests/test_web_functional.py              1552   1552     34      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               266    266     12      0     0%\ntests/test_web_request.py                  490    490      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 885    885      6      0     0%\ntests/test_web_runner.py                   151    151      4      0     0%\ntests/test_web_sendfile.py                  84     84      0      0     0%\ntests/test_web_sendfile_functional.py      655    655     18      0     0%\ntests/test_web_server.py                   162    162      0      0     0%\ntests/test_web_urldispatcher.py            517    517     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     769    769     28      0     0%\ntests/test_websocket_handshake.py          152    152     12      0     0%\ntests/test_websocket_parser.py             300    300     12      0     0%\ntests/test_websocket_writer.py              95     95      6      0     0%\ntests/test_worker.py                       191    191      2      0     0%\n--------------------------------------------------------------------------\nTOTAL                                    34179  29162   4335    420    15%\n\n============================= slowest 10 durations =============================\n0.02s call     r2e_tests/test_1.py::test_app_ctor\n0.02s call     r2e_tests/test_1.py::test_cleanup_ctx\n0.02s call     r2e_tests/test_1.py::test_set_loop\n0.02s teardown r2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop]\n0.02s call     r2e_tests/test_1.py::test_on_shutdown\n0.02s call     r2e_tests/test_1.py::test_set_loop_default_loop\n0.02s call     r2e_tests/test_1.py::test_app_make_handler_debug_exc[True]\n0.02s call     r2e_tests/test_1.py::test_cleanup_ctx_exception_on_startup\n0.02s call     r2e_tests/test_1.py::test_app_make_handler_access_log_class\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_app_ctor\nPASSED r2e_tests/test_1.py::test_app_call\nPASSED r2e_tests/test_1.py::test_app_default_loop\nPASSED r2e_tests/test_1.py::test_set_loop\nPASSED r2e_tests/test_1.py::test_set_loop_default_loop\nPASSED r2e_tests/test_1.py::test_set_loop_with_different_loops\nPASSED r2e_tests/test_1.py::test_app_make_handler_debug_exc[True]\nPASSED r2e_tests/test_1.py::test_app_make_handler_debug_exc[False]\nPASSED r2e_tests/test_1.py::test_app_make_handler_args\nPASSED r2e_tests/test_1.py::test_app_make_handler_access_log_class\nPASSED r2e_tests/test_1.py::test_app_make_handler_raises_deprecation_warning\nPASSED r2e_tests/test_1.py::test_app_register_on_finish\nPASSED r2e_tests/test_1.py::test_app_register_coro\nPASSED r2e_tests/test_1.py::test_non_default_router\nPASSED r2e_tests/test_1.py::test_logging\nPASSED r2e_tests/test_1.py::test_on_shutdown\nPASSED r2e_tests/test_1.py::test_on_startup\nPASSED r2e_tests/test_1.py::test_appkey\nPASSED r2e_tests/test_1.py::test_appkey_repr_concrete\nPASSED r2e_tests/test_1.py::test_appkey_repr_nonconcrete\nPASSED r2e_tests/test_1.py::test_appkey_repr_annotated\nPASSED r2e_tests/test_1.py::test_app_str_keys\nPASSED r2e_tests/test_1.py::test_app_get\nPASSED r2e_tests/test_1.py::test_app_freeze\nPASSED r2e_tests/test_1.py::test_equality\nPASSED r2e_tests/test_1.py::test_app_run_middlewares\nPASSED r2e_tests/test_1.py::test_subapp_pre_frozen_after_adding\nPASSED r2e_tests/test_1.py::test_app_inheritance\nPASSED r2e_tests/test_1.py::test_cleanup_ctx\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_startup\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_cleanup_after_exception\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[Exception]\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError]\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_multiple_yields\nPASSED r2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop]\nPASSED r2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop]\nPASSED r2e_tests/test_1.py::test_subapp_on_startup[pyloop]\nPASSED r2e_tests/test_1.py::test_app_iter\nPASSED r2e_tests/test_1.py::test_app_boolean\nSKIPPED [1] r2e_tests/test_1.py:365: The check is applied in DEBUG mode only\n======================== 40 passed, 1 skipped in 4.59s =========================\n",
    "new_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.2, pluggy-1.5.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f\nconfigfile: setup.cfg\nplugins: cov-5.0.0, asyncio-0.25.0, mock-3.14.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollecting ... collected 41 items\n\nr2e_tests/test_1.py::test_app_ctor PASSED                                [  2%]\nr2e_tests/test_1.py::test_app_call PASSED                                [  4%]\nr2e_tests/test_1.py::test_app_default_loop PASSED                        [  7%]\nr2e_tests/test_1.py::test_set_loop PASSED                                [  9%]\nr2e_tests/test_1.py::test_set_loop_default_loop PASSED                   [ 12%]\nr2e_tests/test_1.py::test_set_loop_with_different_loops PASSED           [ 14%]\nr2e_tests/test_1.py::test_app_make_handler_debug_exc[True] PASSED        [ 17%]\nr2e_tests/test_1.py::test_app_make_handler_debug_exc[False] PASSED       [ 19%]\nr2e_tests/test_1.py::test_app_make_handler_args PASSED                   [ 21%]\nr2e_tests/test_1.py::test_app_make_handler_access_log_class PASSED       [ 24%]\nr2e_tests/test_1.py::test_app_make_handler_raises_deprecation_warning PASSED [ 26%]\nr2e_tests/test_1.py::test_app_register_on_finish PASSED                  [ 29%]\nr2e_tests/test_1.py::test_app_register_coro PASSED                       [ 31%]\nr2e_tests/test_1.py::test_non_default_router PASSED                      [ 34%]\nr2e_tests/test_1.py::test_logging PASSED                                 [ 36%]\nr2e_tests/test_1.py::test_on_shutdown PASSED                             [ 39%]\nr2e_tests/test_1.py::test_on_startup PASSED                              [ 41%]\nr2e_tests/test_1.py::test_appkey PASSED                                  [ 43%]\nr2e_tests/test_1.py::test_appkey_repr_concrete PASSED                    [ 46%]\nr2e_tests/test_1.py::test_appkey_repr_nonconcrete PASSED                 [ 48%]\nr2e_tests/test_1.py::test_appkey_repr_annotated PASSED                   [ 51%]\nr2e_tests/test_1.py::test_app_str_keys PASSED                            [ 53%]\nr2e_tests/test_1.py::test_app_get PASSED                                 [ 56%]\nr2e_tests/test_1.py::test_app_freeze PASSED                              [ 58%]\nr2e_tests/test_1.py::test_equality PASSED                                [ 60%]\nr2e_tests/test_1.py::test_app_run_middlewares PASSED                     [ 63%]\nr2e_tests/test_1.py::test_subapp_pre_frozen_after_adding PASSED          [ 65%]\nr2e_tests/test_1.py::test_app_inheritance PASSED                         [ 68%]\nr2e_tests/test_1.py::test_app_custom_attr SKIPPED (The check is appl...) [ 70%]\nr2e_tests/test_1.py::test_cleanup_ctx PASSED                             [ 73%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_startup PASSED        [ 75%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup PASSED        [ 78%]\nr2e_tests/test_1.py::test_cleanup_ctx_cleanup_after_exception PASSED     [ 80%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[Exception] PASSED [ 82%]\nr2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError] FAILED [ 85%]\nr2e_tests/test_1.py::test_cleanup_ctx_multiple_yields FAILED             [ 87%]\nr2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop] PASSED [ 90%]\nr2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop] PASSED [ 92%]\nr2e_tests/test_1.py::test_subapp_on_startup[pyloop] PASSED               [ 95%]\nr2e_tests/test_1.py::test_app_iter PASSED                                [ 97%]\nr2e_tests/test_1.py::test_app_boolean PASSED                             [100%]\n\n=================================== FAILURES ===================================\n________ test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError] ________\n\nexc_cls = <class 'asyncio.exceptions.CancelledError'>\n\n    @pytest.mark.parametrize(\"exc_cls\", (Exception, asyncio.CancelledError))\n    async def test_cleanup_ctx_exception_on_cleanup_multiple(\n        exc_cls: Type[BaseException],\n    ) -> None:\n        app = web.Application()\n        out = []\n    \n        def f(\n            num: int, fail: bool = False\n        ) -> Callable[[web.Application], AsyncIterator[None]]:\n            async def inner(app: web.Application) -> AsyncIterator[None]:\n                out.append(\"pre_\" + str(num))\n                yield None\n                out.append(\"post_\" + str(num))\n                if fail:\n                    raise exc_cls(\"fail_\" + str(num))\n    \n            return inner\n    \n        app.cleanup_ctx.append(f(1))\n        app.cleanup_ctx.append(f(2, True))\n        app.cleanup_ctx.append(f(3, True))\n        app.freeze()\n        await app.startup()\n        assert out == [\"pre_1\", \"pre_2\", \"pre_3\"]\n        with pytest.raises(web.CleanupError) as ctx:\n>           await app.cleanup()\n\napp        = <Application 0x74bfac0e6790>\nctx        = <ExceptionInfo for raises contextmanager>\nexc_cls    = <class 'asyncio.exceptions.CancelledError'>\nf          = <function test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f at 0x74bfac0b65e0>\nout        = ['pre_1', 'pre_2', 'pre_3', 'post_3']\n\nr2e_tests/test_1.py:505: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \naiohttp/web_app.py:465: in cleanup\n    await self.on_cleanup.send(self)\n        self       = <Application 0x74bfac0e6790>\n.venv/lib/python3.9/site-packages/aiosignal/__init__.py:36: in send\n    await receiver(*args, **kwargs)  # type: ignore\n        args       = (<Application 0x74bfac0e6790>,)\n        kwargs     = {}\n        receiver   = <bound method CleanupContext._on_cleanup of <FrozenList(frozen=True, [<function test_cleanup_ctx_exception_on_cleanup_...4bfac0b60d0>, <function test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f.<locals>.inner at 0x74bfac0b6700>])>>\n        self       = <Signal owner=<Application 0x74bfac0e6790>, frozen=True, [<bound method CleanupContext._on_cleanup of <FrozenList(froz...fac0b60d0>, <function test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f.<locals>.inner at 0x74bfac0b6700>])>>]>\naiohttp/web_app.py:579: in _on_cleanup\n    await it.__anext__()\n        app        = <Application 0x74bfac0e6790>\n        errors     = []\n        it         = <async_generator object test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f.<locals>.inner at 0x74bfac0b6b80>\n        self       = <FrozenList(frozen=True, [<function test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f.<locals>.inner at 0x74bf...74bfac0b60d0>, <function test_cleanup_ctx_exception_on_cleanup_multiple.<locals>.f.<locals>.inner at 0x74bfac0b6700>])>\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\napp = <Application 0x74bfac0e6790>\n\n    async def inner(app: web.Application) -> AsyncIterator[None]:\n        out.append(\"pre_\" + str(num))\n        yield None\n        out.append(\"post_\" + str(num))\n        if fail:\n>           raise exc_cls(\"fail_\" + str(num))\nE           asyncio.exceptions.CancelledError: fail_3\n\napp        = <Application 0x74bfac0e6790>\nexc_cls    = <class 'asyncio.exceptions.CancelledError'>\nfail       = True\nnum        = 3\nout        = ['pre_1', 'pre_2', 'pre_3', 'post_3']\n\nr2e_tests/test_1.py:494: CancelledError\n\nDuring handling of the above exception, another exception occurred:\n\npyfuncitem = <Function test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError]>\n\n    def pytest_pyfunc_call(pyfuncitem):  # type: ignore[no-untyped-def]\n        \"\"\"Run coroutines in an event loop instead of a normal function call.\"\"\"\n        fast = pyfuncitem.config.getoption(\"--aiohttp-fast\")\n        if asyncio.iscoroutinefunction(pyfuncitem.function):\n            existing_loop = pyfuncitem.funcargs.get(\n                \"proactor_loop\"\n            ) or pyfuncitem.funcargs.get(\"loop\", None)\n            with _runtime_warning_context():\n                with _passthrough_loop_context(existing_loop, fast=fast) as _loop:\n                    testargs = {\n                        arg: pyfuncitem.funcargs[arg]\n                        for arg in pyfuncitem._fixtureinfo.argnames\n                    }\n>                   _loop.run_until_complete(pyfuncitem.obj(**testargs))\n\n_loop      = <_UnixSelectorEventLoop running=False closed=False debug=False>\nexisting_loop = None\nfast       = False\npyfuncitem = <Function test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError]>\ntestargs   = {'exc_cls': <class 'asyncio.exceptions.CancelledError'>}\n\naiohttp/pytest_plugin.py:206: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <_UnixSelectorEventLoop running=False closed=False debug=False>\nfuture = <Task cancelled name='Task-19' coro=<test_cleanup_ctx_exception_on_cleanup_multiple() done, defined at /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/r2e_tests/test_1.py:479>>\n\n    def run_until_complete(self, future):\n        \"\"\"Run until the Future is done.\n    \n        If the argument is a coroutine, it is wrapped in a Task.\n    \n        WARNING: It would be disastrous to call run_until_complete()\n        with the same coroutine twice -- it would wrap it in two\n        different Tasks and that can't be good.\n    \n        Return the Future's result, or raise its exception.\n        \"\"\"\n        self._check_closed()\n        self._check_running()\n    \n        new_task = not futures.isfuture(future)\n        future = tasks.ensure_future(future, loop=self)\n        if new_task:\n            # An exception is raised if the future didn't complete, so there\n            # is no need to log the \"destroy pending task\" message\n            future._log_destroy_pending = False\n    \n        future.add_done_callback(_run_until_complete_cb)\n        try:\n            self.run_forever()\n        except:\n            if new_task and future.done() and not future.cancelled():\n                # The coroutine raised a BaseException. Consume the exception\n                # to not log a warning, the caller doesn't have access to the\n                # local task.\n                future.exception()\n            raise\n        finally:\n            future.remove_done_callback(_run_until_complete_cb)\n        if not future.done():\n            raise RuntimeError('Event loop stopped before Future completed.')\n    \n>       return future.result()\nE       asyncio.exceptions.CancelledError\n\nfuture     = <Task cancelled name='Task-19' coro=<test_cleanup_ctx_exception_on_cleanup_multiple() done, defined at /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/r2e_tests/test_1.py:479>>\nnew_task   = True\nself       = <_UnixSelectorEventLoop running=False closed=False debug=False>\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/asyncio/base_events.py:647: CancelledError\n_______________________ test_cleanup_ctx_multiple_yields _______________________\n\ncls = <class '_pytest.runner.CallInfo'>\nfunc = <function call_and_report.<locals>.<lambda> at 0x74bfac12e550>\nwhen = 'call'\nreraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)\n\n    @classmethod\n    def from_call(\n        cls,\n        func: Callable[[], TResult],\n        when: Literal[\"collect\", \"setup\", \"call\", \"teardown\"],\n        reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None,\n    ) -> CallInfo[TResult]:\n        \"\"\"Call func, wrapping the result in a CallInfo.\n    \n        :param func:\n            The function to call. Called without arguments.\n        :type func: Callable[[], _pytest.runner.TResult]\n        :param when:\n            The phase in which the function is called.\n        :param reraise:\n            Exception or exceptions that shall propagate if raised by the\n            function, instead of being wrapped in the CallInfo.\n        \"\"\"\n        excinfo = None\n        start = timing.time()\n        precise_start = timing.perf_counter()\n        try:\n>           result: TResult | None = func()\n\ncls        = <class '_pytest.runner.CallInfo'>\nduration   = 0.023803119009244256\nexcinfo    = <ExceptionInfo PytestUnraisableExceptionWarning('Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_U...rning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n') tblen=12>\nfunc       = <function call_and_report.<locals>.<lambda> at 0x74bfac12e550>\nprecise_start = 90058.308792263\nprecise_stop = 90058.332595382\nreraise    = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)\nresult     = None\nstart      = 1735791701.9276047\nstop       = 1735791701.951409\nwhen       = 'call'\n\n.venv/lib/python3.9/site-packages/_pytest/runner.py:341: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.venv/lib/python3.9/site-packages/_pytest/runner.py:242: in <lambda>\n    lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise\n        item       = <Function test_cleanup_ctx_multiple_yields>\n        kwds       = {}\n        runtest_hook = <HookCaller 'pytest_runtest_call'>\n.venv/lib/python3.9/site-packages/pluggy/_hooks.py:513: in __call__\n    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)\n        firstresult = False\n        kwargs     = {'item': <Function test_cleanup_ctx_multiple_yields>}\n        self       = <HookCaller 'pytest_runtest_call'>\n.venv/lib/python3.9/site-packages/pluggy/_manager.py:120: in _hookexec\n    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)\n        firstresult = False\n        hook_name  = 'pytest_runtest_call'\n        kwargs     = {'item': <Function test_cleanup_ctx_multiple_yields>}\n        methods    = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/home/gcpuser/buckets/local_repoeval_bucket/rep...http_87342c791dfd4c916877ba3dffafb9345bb0491f/.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py'>>, ...]\n        self       = <_pytest.config.PytestPluginManager object at 0x74bfae7e2610>\n.venv/lib/python3.9/site-packages/_pytest/threadexception.py:92: in pytest_runtest_call\n    yield from thread_exception_runtest_hook()\n.venv/lib/python3.9/site-packages/_pytest/threadexception.py:68: in thread_exception_runtest_hook\n    yield\n        cm         = <_pytest.threadexception.catch_threading_exception object at 0x74bfabfbee80>\n.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py:95: in pytest_runtest_call\n    yield from unraisable_exception_runtest_hook()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\n    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:\n        with catch_unraisable_exception() as cm:\n            try:\n                yield\n            finally:\n                if cm.unraisable:\n                    if cm.unraisable.err_msg is not None:\n                        err_msg = cm.unraisable.err_msg\n                    else:\n                        err_msg = \"Exception ignored in\"\n                    msg = f\"{err_msg}: {cm.unraisable.object!r}\\n\\n\"\n                    msg += \"\".join(\n                        traceback.format_exception(\n                            cm.unraisable.exc_type,\n                            cm.unraisable.exc_value,\n                            cm.unraisable.exc_traceback,\n                        )\n                    )\n>                   warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))\nE                   pytest.PytestUnraisableExceptionWarning: Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\nE                   \nE                   Traceback (most recent call last):\nE                     File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/aiohttp/test_utils.py\", line 587, in teardown_test_loop\nE                       gc.collect()\nE                   ResourceWarning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\n\ncm         = <_pytest.unraisableexception.catch_unraisable_exception object at 0x74bfabfbed90>\nerr_msg    = 'Exception ignored in'\nmsg        = 'Exception ignored in: <socket.socket fd=-1, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n\\nTr...nResourceWarning: unclosed <socket.socket fd=14, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>\\n'\n\n.venv/lib/python3.9/site-packages/_pytest/unraisableexception.py:85: PytestUnraisableExceptionWarning\n==================================== PASSES ====================================\n\n---------- coverage: platform linux, python 3.9.21-final-0 -----------\nName                                     Stmts   Miss Branch BrPart  Cover\n--------------------------------------------------------------------------\naiohttp/__init__.py                         27     10      2      0    59%\naiohttp/abc.py                              97      2      0      0    98%\naiohttp/base_protocol.py                    67     36     22      3    38%\naiohttp/client.py                          545    264    196     51    45%\naiohttp/client_exceptions.py               154     65     20      1    52%\naiohttp/client_proto.py                    165     54     56     19    60%\naiohttp/client_reqrep.py                   695    310    300     73    47%\naiohttp/client_ws.py                       259    212     80      1    14%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       770    441    319     49    36%\naiohttp/cookiejar.py                       255    174    118      9    24%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         501    223    172     24    49%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     504    404    208      1    14%\naiohttp/http_websocket.py                  390    285    154      0    20%\naiohttp/http_writer.py                     118     44     50     13    53%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       623    508    266      1    13%\naiohttp/payload.py                         237    113     58      1    44%\naiohttp/payload_streamer.py                 31     11      4      0    69%\naiohttp/pytest_plugin.py                   162     60     42      6    61%\naiohttp/resolver.py                         80     55     26      2    25%\naiohttp/streams.py                         398    250    146     11    29%\naiohttp/tcp_helpers.py                      19      2      6      3    80%\naiohttp/test_utils.py                      315    115     50     14    60%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             118     81     52      0    23%\naiohttp/web_app.py                         282     22     74     10    88%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     37     20      1    59%\naiohttp/web_middlewares.py                  55     37     20      0    24%\naiohttp/web_protocol.py                    354    132    155     48    53%\naiohttp/web_request.py                     441    241    134      8    37%\naiohttp/web_response.py                    479    258    220     33    36%\naiohttp/web_routedef.py                    100     39     10      1    60%\naiohttp/web_runner.py                      213     72     36     10    62%\naiohttp/web_server.py                       42      6     12      2    78%\naiohttp/web_urldispatcher.py               742    341    161     23    49%\naiohttp/web_ws.py                          382    310    132      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          130    130     16      0     0%\ntests/test_base_protocol.py                198    198      2      0     0%\ntests/test_circular_imports.py              29     29     13      0     0%\ntests/test_classbasedview.py                39     39      0      0     0%\ntests/test_client_connection.py             94     94      2      0     0%\ntests/test_client_exceptions.py            199    199     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2455   2455     86      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               808    808     18      0     0%\ntests/test_client_response.py              466    466      4      0     0%\ntests/test_client_session.py               556    556     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         750    750     32      0     0%\ntests/test_connector.py                   1676   1676     82      0     0%\ntests/test_cookiejar.py                    350    350     24      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      81     81      6      0     0%\ntests/test_helpers.py                      470    470     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1088   1088     66      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    826    826     32      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       95     95      0      0     0%\ntests/test_proxy.py                        301    301      0      0     0%\ntests/test_proxy_functional.py             459    459     18      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     193    193     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      554    554     30      0     0%\ntests/test_streams.py                     1076   1076     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   244    244      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  851    851     24      0     0%\ntests/test_web_app.py                      453    453      6      0     0%\ntests/test_web_cli.py                       75     75      0      0     0%\ntests/test_web_exceptions.py               176    176     22      0     0%\ntests/test_web_functional.py              1552   1552     34      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               266    266     12      0     0%\ntests/test_web_request.py                  490    490      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 885    885      6      0     0%\ntests/test_web_runner.py                   151    151      4      0     0%\ntests/test_web_sendfile.py                  84     84      0      0     0%\ntests/test_web_sendfile_functional.py      655    655     18      0     0%\ntests/test_web_server.py                   162    162      0      0     0%\ntests/test_web_urldispatcher.py            517    517     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     769    769     28      0     0%\ntests/test_websocket_handshake.py          152    152     12      0     0%\ntests/test_websocket_parser.py             300    300     12      0     0%\ntests/test_websocket_writer.py              95     95      6      0     0%\ntests/test_worker.py                       191    191      2      0     0%\n--------------------------------------------------------------------------\nTOTAL                                    34178  29159   4335    420    15%\n\n============================= slowest 10 durations =============================\n0.02s call     r2e_tests/test_1.py::test_app_ctor\n0.02s call     r2e_tests/test_1.py::test_cleanup_ctx_multiple_yields\n0.02s teardown r2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop]\n0.02s call     r2e_tests/test_1.py::test_cleanup_ctx\n0.02s teardown r2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop]\n0.02s call     r2e_tests/test_1.py::test_set_loop\n0.02s call     r2e_tests/test_1.py::test_app_make_handler_debug_exc[False]\n0.02s call     r2e_tests/test_1.py::test_app_make_handler_debug_exc[True]\n0.02s call     r2e_tests/test_1.py::test_on_shutdown\n0.02s teardown r2e_tests/test_1.py::test_subapp_on_startup[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_app_ctor\nPASSED r2e_tests/test_1.py::test_app_call\nPASSED r2e_tests/test_1.py::test_app_default_loop\nPASSED r2e_tests/test_1.py::test_set_loop\nPASSED r2e_tests/test_1.py::test_set_loop_default_loop\nPASSED r2e_tests/test_1.py::test_set_loop_with_different_loops\nPASSED r2e_tests/test_1.py::test_app_make_handler_debug_exc[True]\nPASSED r2e_tests/test_1.py::test_app_make_handler_debug_exc[False]\nPASSED r2e_tests/test_1.py::test_app_make_handler_args\nPASSED r2e_tests/test_1.py::test_app_make_handler_access_log_class\nPASSED r2e_tests/test_1.py::test_app_make_handler_raises_deprecation_warning\nPASSED r2e_tests/test_1.py::test_app_register_on_finish\nPASSED r2e_tests/test_1.py::test_app_register_coro\nPASSED r2e_tests/test_1.py::test_non_default_router\nPASSED r2e_tests/test_1.py::test_logging\nPASSED r2e_tests/test_1.py::test_on_shutdown\nPASSED r2e_tests/test_1.py::test_on_startup\nPASSED r2e_tests/test_1.py::test_appkey\nPASSED r2e_tests/test_1.py::test_appkey_repr_concrete\nPASSED r2e_tests/test_1.py::test_appkey_repr_nonconcrete\nPASSED r2e_tests/test_1.py::test_appkey_repr_annotated\nPASSED r2e_tests/test_1.py::test_app_str_keys\nPASSED r2e_tests/test_1.py::test_app_get\nPASSED r2e_tests/test_1.py::test_app_freeze\nPASSED r2e_tests/test_1.py::test_equality\nPASSED r2e_tests/test_1.py::test_app_run_middlewares\nPASSED r2e_tests/test_1.py::test_subapp_pre_frozen_after_adding\nPASSED r2e_tests/test_1.py::test_app_inheritance\nPASSED r2e_tests/test_1.py::test_cleanup_ctx\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_startup\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_cleanup_after_exception\nPASSED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[Exception]\nPASSED r2e_tests/test_1.py::test_subapp_chained_config_dict_visibility[pyloop]\nPASSED r2e_tests/test_1.py::test_subapp_chained_config_dict_overriding[pyloop]\nPASSED r2e_tests/test_1.py::test_subapp_on_startup[pyloop]\nPASSED r2e_tests/test_1.py::test_app_iter\nPASSED r2e_tests/test_1.py::test_app_boolean\nSKIPPED [1] r2e_tests/test_1.py:365: The check is applied in DEBUG mode only\nFAILED r2e_tests/test_1.py::test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError]\nFAILED r2e_tests/test_1.py::test_cleanup_ctx_multiple_yields - pytest.PytestU...\n=================== 2 failed, 38 passed, 1 skipped in 5.04s ====================\n",
    "old_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_87342c791dfd4c916877ba3dffafb9345bb0491f/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n"
}
CHANGES/8908.bugfix.rst aiohttp/web_app.py docs/web_advanced.rst tests/test_web_app.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** CancelledError in Cleanup Context Prevents Completion of Other Cleanup Contexts

**Description:**
When using multiple cleanup contexts in an application, raising an `asyncio.CancelledError` within one cleanup context prevents the remaining cleanup contexts from completing. This leads to incomplete cleanup and potentially leaves resources improperly released.

**Example Code:**
```python
import asyncio
from aiohttp import web

async def cleanup_context_one(app: web.Application):
    print("Starting cleanup context one")
    yield
    print("Completed cleanup context one")

async def cleanup_context_two(app: web.Application):
    print("Starting cleanup context two")
    yield
    raise asyncio.CancelledError("Cleanup interrupted in context two")

async def cleanup_context_three(app: web.Application):
    print("Starting cleanup context three")
    yield
    print("Completed cleanup context three")

app = web.Application()
app.cleanup_ctx.append(cleanup_context_one)
app.cleanup_ctx.append(cleanup_context_two)
app.cleanup_ctx.append(cleanup_context_three)

async def run_app():
    await app.startup()
    await app.cleanup()

asyncio.run(run_app())
```

**Expected Behavior:**
All cleanup contexts should complete their execution regardless of exceptions raised in any individual context. Specifically, even if one cleanup context raises an `asyncio.CancelledError`, the other cleanup contexts should still run to completion.

**Actual Behavior:**
When a cleanup context raises an `asyncio.CancelledError`, it stops the execution of subsequent cleanup contexts. In the example above, "Completed cleanup context three" is never printed because the exception in `cleanup_context_two` halts the cleanup process.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### aiohttp/web_app.py
import asyncio
import logging
import warnings
from functools import partial, update_wrapper
from typing import (
    TYPE_CHECKING,
    Any,
    AsyncIterator,
    Awaitable,
    Callable,
    Dict,
    Iterable,
    Iterator,
    List,
    Mapping,
    MutableMapping,
    Optional,
    Sequence,
    Tuple,
    Type,
    TypeVar,
    Union,
    cast,
    overload,
)

from aiosignal import Signal
from frozenlist import FrozenList

from . import hdrs
from .abc import (
    AbstractAccessLogger,
    AbstractMatchInfo,
    AbstractRouter,
    AbstractStreamWriter,
)
from .helpers import DEBUG, AppKey
from .http_parser import RawRequestMessage
from .log import web_logger
from .streams import StreamReader
from .typedefs import Middleware
from .web_exceptions import NotAppKeyWarning
from .web_log import AccessLogger
from .web_middlewares import _fix_request_current_app
from .web_protocol import RequestHandler
from .web_request import Request
from .web_response import StreamResponse
from .web_routedef import AbstractRouteDef
from .web_server import Server
from .web_urldispatcher import (
    AbstractResource,
    AbstractRoute,
    Domain,
    MaskDomain,
    MatchedSubAppResource,
    PrefixedSubAppResource,
    UrlDispatcher,
)

__all__ = ("Application", "CleanupError")


if TYPE_CHECKING:
    _AppSignal = Signal[Callable[["Application"], Awaitable[None]]]
    _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]
    _Middlewares = FrozenList[Middleware]
    _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]]
    _Subapps = List["Application"]
else:
    # No type checker mode, skip types
    _AppSignal = Signal
    _RespPrepareSignal = Signal
    _Middlewares = FrozenList
    _MiddlewaresHandlers = Optional[Sequence]
    _Subapps = List

_T = TypeVar("_T")
_U = TypeVar("_U")
_Resource = TypeVar("_Resource", bound=AbstractResource)


class Application(MutableMapping[Union[str, AppKey[Any]], Any]):
    ATTRS = frozenset(
        [
            "logger",
            "_debug",
            "_router",
            "_loop",
            "_handler_args",
            "_middlewares",
            "_middlewares_handlers",
            "_run_middlewares",
            "_state",
            "_frozen",
            "_pre_frozen",
            "_subapps",
            "_on_response_prepare",
            "_on_startup",
            "_on_shutdown",
            "_on_cleanup",
            "_client_max_size",
            "_cleanup_ctx",
        ]
    )

    def __init__(
        self,
        *,
        logger: logging.Logger = web_logger,
        router: Optional[UrlDispatcher] = None,
        middlewares: Iterable[Middleware] = (),
        handler_args: Optional[Mapping[str, Any]] = None,
        client_max_size: int = 1024**2,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        debug: Any = ...,  # mypy doesn't support ellipsis
    ) -> None:
        if router is None:
            router = UrlDispatcher()
        else:
            warnings.warn(
                "router argument is deprecated", DeprecationWarning, stacklevel=2
            )
        assert isinstance(router, AbstractRouter), router

        if loop is not None:
            warnings.warn(
                "loop argument is deprecated", DeprecationWarning, stacklevel=2
            )

        if debug is not ...:
            warnings.warn(
                "debug argument is deprecated", DeprecationWarning, stacklevel=2
            )
        self._debug = debug
        self._router: UrlDispatcher = router
        self._loop = loop
        self._handler_args = handler_args
        self.logger = logger

        self._middlewares: _Middlewares = FrozenList(middlewares)

        # initialized on freezing
        self._middlewares_handlers: _MiddlewaresHandlers = None
        # initialized on freezing
        self._run_middlewares: Optional[bool] = None

        self._state: Dict[Union[AppKey[Any], str], object] = {}
        self._frozen = False
        self._pre_frozen = False
        self._subapps: _Subapps = []

        self._on_response_prepare: _RespPrepareSignal = Signal(self)
        self._on_startup: _AppSignal = Signal(self)
        self._on_shutdown: _AppSignal = Signal(self)
        self._on_cleanup: _AppSignal = Signal(self)
        self._cleanup_ctx = CleanupContext()
        self._on_startup.append(self._cleanup_ctx._on_startup)
        self._on_cleanup.append(self._cleanup_ctx._on_cleanup)
        self._client_max_size = client_max_size

    def __init_subclass__(cls: Type["Application"]) -> None:
        warnings.warn(
            "Inheritance class {} from web.Application "
            "is discouraged".format(cls.__name__),
            DeprecationWarning,
            stacklevel=3,
        )

    if DEBUG:  # pragma: no cover

        def __setattr__(self, name: str, val: Any) -> None:
            if name not in self.ATTRS:
                warnings.warn(
                    "Setting custom web.Application.{} attribute "
                    "is discouraged".format(name),
                    DeprecationWarning,
                    stacklevel=2,
                )
            super().__setattr__(name, val)

    # MutableMapping API

    def __eq__(self, other: object) -> bool:
        return self is other

    @overload  # type: ignore[override]
    def __getitem__(self, key: AppKey[_T]) -> _T: ...

    @overload
    def __getitem__(self, key: str) -> Any: ...

    def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:
        return self._state[key]

    def _check_frozen(self) -> None:
        if self._frozen:
            warnings.warn(
                "Changing state of started or joined " "application is deprecated",
                DeprecationWarning,
                stacklevel=3,
            )

    @overload  # type: ignore[override]
    def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...

    @overload
    def __setitem__(self, key: str, value: Any) -> None: ...

    def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:
        self._check_frozen()
        if not isinstance(key, AppKey):
            warnings.warn(
                "It is recommended to use web.AppKey instances for keys.\n"
                + "https://docs.aiohttp.org/en/stable/web_advanced.html"
                + "#application-s-config",
                category=NotAppKeyWarning,
                stacklevel=2,
            )
        self._state[key] = value

    def __delitem__(self, key: Union[str, AppKey[_T]]) -> None:
        self._check_frozen()
        del self._state[key]

    def __len__(self) -> int:
        return len(self._state)

    def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]:
        return iter(self._state)

    @overload  # type: ignore[override]
    def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...

    @overload
    def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...

    @overload
    def get(self, key: str, default: Any = ...) -> Any: ...

    def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:
        return self._state.get(key, default)

    ########
    @property
    def loop(self) -> asyncio.AbstractEventLoop:
        # Technically the loop can be None
        # but we mask it by explicit type cast
        # to provide more convenient type annotation
        warnings.warn("loop property is deprecated", DeprecationWarning, stacklevel=2)
        return cast(asyncio.AbstractEventLoop, self._loop)

    def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:
        if loop is None:
            loop = asyncio.get_event_loop()
        if self._loop is not None and self._loop is not loop:
            raise RuntimeError(
                "web.Application instance initialized with different loop"
            )

        self._loop = loop

        # set loop debug
        if self._debug is ...:
            self._debug = loop.get_debug()

        # set loop to sub applications
        for subapp in self._subapps:
            subapp._set_loop(loop)

    @property
    def pre_frozen(self) -> bool:
        return self._pre_frozen

    def pre_freeze(self) -> None:
        if self._pre_frozen:
            return

        self._pre_frozen = True
        self._middlewares.freeze()
        self._router.freeze()
        self._on_response_prepare.freeze()
        self._cleanup_ctx.freeze()
        self._on_startup.freeze()
        self._on_shutdown.freeze()
        self._on_cleanup.freeze()
        self._middlewares_handlers = tuple(self._prepare_middleware())

        # If current app and any subapp do not have middlewares avoid run all
        # of the code footprint that it implies, which have a middleware
        # hardcoded per app that sets up the current_app attribute. If no
        # middlewares are configured the handler will receive the proper
        # current_app without needing all of this code.
        self._run_middlewares = True if self.middlewares else False

        for subapp in self._subapps:
            subapp.pre_freeze()
            self._run_middlewares = self._run_middlewares or subapp._run_middlewares

    @property
    def frozen(self) -> bool:
        return self._frozen

    def freeze(self) -> None:
        if self._frozen:
            return

        self.pre_freeze()
        self._frozen = True
        for subapp in self._subapps:
            subapp.freeze()

    @property
    def debug(self) -> bool:
        warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2)
        return self._debug  # type: ignore[no-any-return]

    def _reg_subapp_signals(self, subapp: "Application") -> None:
        def reg_handler(signame: str) -> None:
            subsig = getattr(subapp, signame)

            async def handler(app: "Application") -> None:
                await subsig.send(subapp)

            appsig = getattr(self, signame)
            appsig.append(handler)

        reg_handler("on_startup")
        reg_handler("on_shutdown")
        reg_handler("on_cleanup")

    def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource:
        if not isinstance(prefix, str):
            raise TypeError("Prefix must be str")
        prefix = prefix.rstrip("/")
        if not prefix:
            raise ValueError("Prefix cannot be empty")
        factory = partial(PrefixedSubAppResource, prefix, subapp)
        return self._add_subapp(factory, subapp)

    def _add_subapp(
        self, resource_factory: Callable[[], _Resource], subapp: "Application"
    ) -> _Resource:
        if self.frozen:
            raise RuntimeError("Cannot add sub application to frozen application")
        if subapp.frozen:
            raise RuntimeError("Cannot add frozen application")
        resource = resource_factory()
        self.router.register_resource(resource)
        self._reg_subapp_signals(subapp)
        self._subapps.append(subapp)
        subapp.pre_freeze()
        if self._loop is not None:
            subapp._set_loop(self._loop)
        return resource

    def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource:
        if not isinstance(domain, str):
            raise TypeError("Domain must be str")
        elif "*" in domain:
            rule: Domain = MaskDomain(domain)
        else:
            rule = Domain(domain)
        factory = partial(MatchedSubAppResource, rule, subapp)
        return self._add_subapp(factory, subapp)

    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:
        return self.router.add_routes(routes)

    @property
    def on_response_prepare(self) -> _RespPrepareSignal:
        return self._on_response_prepare

    @property
    def on_startup(self) -> _AppSignal:
        return self._on_startup

    @property
    def on_shutdown(self) -> _AppSignal:
        return self._on_shutdown

    @property
    def on_cleanup(self) -> _AppSignal:
        return self._on_cleanup

    @property
    def cleanup_ctx(self) -> "CleanupContext":
        return self._cleanup_ctx

    @property
    def router(self) -> UrlDispatcher:
        return self._router

    @property
    def middlewares(self) -> _Middlewares:
        return self._middlewares

    def _make_handler(
        self,
        *,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        access_log_class: Type[AbstractAccessLogger] = AccessLogger,
        **kwargs: Any,
    ) -> Server:

        if not issubclass(access_log_class, AbstractAccessLogger):
            raise TypeError(
                "access_log_class must be subclass of "
                "aiohttp.abc.AbstractAccessLogger, got {}".format(access_log_class)
            )

        self._set_loop(loop)
        self.freeze()

        kwargs["debug"] = self._debug
        kwargs["access_log_class"] = access_log_class
        if self._handler_args:
            for k, v in self._handler_args.items():
                kwargs[k] = v

        return Server(
            self._handle,  # type: ignore[arg-type]
            request_factory=self._make_request,
            loop=self._loop,
            **kwargs,
        )

    def make_handler(
        self,
        *,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        access_log_class: Type[AbstractAccessLogger] = AccessLogger,
        **kwargs: Any,
    ) -> Server:

        warnings.warn(
            "Application.make_handler(...) is deprecated, " "use AppRunner API instead",
            DeprecationWarning,
            stacklevel=2,
        )

        return self._make_handler(
            loop=loop, access_log_class=access_log_class, **kwargs
        )

    async def startup(self) -> None:
        """Causes on_startup signal

        Should be called in the event loop along with the request handler.
        """
        await self.on_startup.send(self)

    async def shutdown(self) -> None:
        """Causes on_shutdown signal

        Should be called before cleanup()
        """
        await self.on_shutdown.send(self)

    async def cleanup(self) -> None:
        """Causes on_cleanup signal

        Should be called after shutdown()
        """
        if self.on_cleanup.frozen:
            await self.on_cleanup.send(self)
        else:
            # If an exception occurs in startup, ensure cleanup contexts are completed.
            await self._cleanup_ctx._on_cleanup(self)

    def _make_request(
        self,
        message: RawRequestMessage,
        payload: StreamReader,
        protocol: RequestHandler,
        writer: AbstractStreamWriter,
        task: "asyncio.Task[None]",
        _cls: Type[Request] = Request,
    ) -> Request:
        return _cls(
            message,
            payload,
            protocol,
            writer,
            task,
            self._loop,
            client_max_size=self._client_max_size,
        )

    def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]:
        for m in reversed(self._middlewares):
            if getattr(m, "__middleware_version__", None) == 1:
                yield m, True
            else:
                warnings.warn(
                    'old-style middleware "{!r}" deprecated, ' "see #2252".format(m),
                    DeprecationWarning,
                    stacklevel=2,
                )
                yield m, False

        yield _fix_request_current_app(self), True

    async def _handle(self, request: Request) -> StreamResponse:
        loop = asyncio.get_event_loop()
        debug = loop.get_debug()
        match_info = await self._router.resolve(request)
        if debug:  # pragma: no cover
            if not isinstance(match_info, AbstractMatchInfo):
                raise TypeError(
                    "match_info should be AbstractMatchInfo "
                    "instance, not {!r}".format(match_info)
                )
        match_info.add_app(self)

        match_info.freeze()

        resp = None
        request._match_info = match_info
        expect = request.headers.get(hdrs.EXPECT)
        if expect:
            resp = await match_info.expect_handler(request)
            await request.writer.drain()

        if resp is None:
            handler = match_info.handler

            if self._run_middlewares:
                for app in match_info.apps[::-1]:
                    for m, new_style in app._middlewares_handlers:  # type: ignore[union-attr]
                        if new_style:
                            handler = update_wrapper(
                                partial(m, handler=handler), handler  # type: ignore[misc]
                            )
                        else:
                            handler = await m(app, handler)  # type: ignore[arg-type,assignment]

            resp = await handler(request)

        return resp

    def __call__(self) -> "Application":
        """gunicorn compatibility"""
        return self

    def __repr__(self) -> str:
        return f"<Application 0x{id(self):x}>"

    def __bool__(self) -> bool:
        return True


class CleanupError(RuntimeError):
    @property
    def exceptions(self) -> List[BaseException]:
        return cast(List[BaseException], self.args[1])


if TYPE_CHECKING:
    _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]]
else:
    _CleanupContextBase = FrozenList


class CleanupContext(_CleanupContextBase):
    def __init__(self) -> None:
        super().__init__()
        self._exits: List[AsyncIterator[None]] = []

    async def _on_startup(self, app: Application) -> None:
        for cb in self:
            it = cb(app).__aiter__()
            await it.__anext__()
            self._exits.append(it)

    async def _on_cleanup(self, app: Application) -> None:
        errors = []
        for it in reversed(self._exits):
            try:
                await it.__anext__()
            except StopAsyncIteration:
                pass
            except Exception as exc:
                errors.append(exc)
            else:
                errors.append(RuntimeError(f"{it!r} has more than one 'yield'"))
        if errors:
            if len(errors) == 1:
                raise errors[0]
            else:
                raise CleanupError("Multiple errors on cleanup stage", errors)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** CancelledError in Cleanup Context Prevents Completion of Other Cleanup Contexts

**Description:**
When using multiple cleanup contexts in an application, raising an `asyncio.CancelledError` within one cleanup context prevents the remaining cleanup contexts from completing. This leads to incomplete cleanup and potentially leaves resources improperly released.

**Example Code:**
```python
import asyncio
from aiohttp import web

async def cleanup_context_one(app: web.Application):
    print("Starting cleanup context one")
    yield
    print("Completed cleanup context one")

async def cleanup_context_two(app: web.Application):
    print("Starting cleanup context two")
    yield
    raise asyncio.CancelledError("Cleanup interrupted in context two")

async def cleanup_context_three(app: web.Application):
    print("Starting cleanup context three")
    yield
    print("Completed cleanup context three")

app = web.Application()
app.cleanup_ctx.append(cleanup_context_one)
app.cleanup_ctx.append(cleanup_context_two)
app.cleanup_ctx.append(cleanup_context_three)

async def run_app():
    await app.startup()
    await app.cleanup()

asyncio.run(run_app())
```

**Expected Behavior:**
All cleanup contexts should complete their execution regardless of exceptions raised in any individual context. Specifically, even if one cleanup context raises an `asyncio.CancelledError`, the other cleanup contexts should still run to completion.

**Actual Behavior:**
When a cleanup context raises an `asyncio.CancelledError`, it stops the execution of subsequent cleanup contexts. In the example above, "Completed cleanup context three" is never printed because the exception in `cleanup_context_two` halts the cleanup process.

[/ISSUE]
{
    "test_app_ctor": "PASSED",
    "test_app_call": "PASSED",
    "test_app_default_loop": "PASSED",
    "test_set_loop": "PASSED",
    "test_set_loop_default_loop": "PASSED",
    "test_set_loop_with_different_loops": "PASSED",
    "test_app_make_handler_debug_exc[True]": "PASSED",
    "test_app_make_handler_debug_exc[False]": "PASSED",
    "test_app_make_handler_args": "PASSED",
    "test_app_make_handler_access_log_class": "PASSED",
    "test_app_make_handler_raises_deprecation_warning": "PASSED",
    "test_app_register_on_finish": "PASSED",
    "test_app_register_coro": "PASSED",
    "test_non_default_router": "PASSED",
    "test_logging": "PASSED",
    "test_on_shutdown": "PASSED",
    "test_on_startup": "PASSED",
    "test_appkey": "PASSED",
    "test_appkey_repr_concrete": "PASSED",
    "test_appkey_repr_nonconcrete": "PASSED",
    "test_appkey_repr_annotated": "PASSED",
    "test_app_str_keys": "PASSED",
    "test_app_get": "PASSED",
    "test_app_freeze": "PASSED",
    "test_equality": "PASSED",
    "test_app_run_middlewares": "PASSED",
    "test_subapp_pre_frozen_after_adding": "PASSED",
    "test_app_inheritance": "PASSED",
    "test_cleanup_ctx": "PASSED",
    "test_cleanup_ctx_exception_on_startup": "PASSED",
    "test_cleanup_ctx_exception_on_cleanup": "PASSED",
    "test_cleanup_ctx_cleanup_after_exception": "PASSED",
    "test_cleanup_ctx_exception_on_cleanup_multiple[Exception]": "PASSED",
    "test_cleanup_ctx_exception_on_cleanup_multiple[CancelledError]": "PASSED",
    "test_cleanup_ctx_multiple_yields": "PASSED",
    "test_subapp_chained_config_dict_visibility[pyloop]": "PASSED",
    "test_subapp_chained_config_dict_overriding[pyloop]": "PASSED",
    "test_subapp_on_startup[pyloop]": "PASSED",
    "test_app_iter": "PASSED",
    "test_app_boolean": "PASSED"
}
diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py
index 3b4b6489..3510bffd 100644
--- a/aiohttp/web_app.py
+++ b/aiohttp/web_app.py
@@ -579,7 +579,7 @@ class CleanupContext(_CleanupContextBase):
                 await it.__anext__()
             except StopAsyncIteration:
                 pass
-            except Exception as exc:
+            except (Exception, asyncio.CancelledError) as exc:
                 errors.append(exc)
             else:
                 errors.append(RuntimeError(f"{it!r} has more than one 'yield'"))

{"aiohttp/web_app.py": "import asyncio\nimport logging\nimport warnings\nfrom functools import partial, update_wrapper\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Awaitable,\n    Callable,\n    Dict,\n    Iterable,\n    Iterator,\n    List,\n    Mapping,\n    MutableMapping,\n    Optional,\n    Sequence,\n    Tuple,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n    overload,\n)\n\nfrom aiosignal import Signal\nfrom frozenlist import FrozenList\n\nfrom . import hdrs\nfrom .abc import (\n    AbstractAccessLogger,\n    AbstractMatchInfo,\n    AbstractRouter,\n    AbstractStreamWriter,\n)\nfrom .helpers import DEBUG, AppKey\nfrom .http_parser import RawRequestMessage\nfrom .log import web_logger\nfrom .streams import StreamReader\nfrom .typedefs import Middleware\nfrom .web_exceptions import NotAppKeyWarning\nfrom .web_log import AccessLogger\nfrom .web_middlewares import _fix_request_current_app\nfrom .web_protocol import RequestHandler\nfrom .web_request import Request\nfrom .web_response import StreamResponse\nfrom .web_routedef import AbstractRouteDef\nfrom .web_server import Server\nfrom .web_urldispatcher import (\n    AbstractResource,\n    AbstractRoute,\n    Domain,\n    MaskDomain,\n    MatchedSubAppResource,\n    PrefixedSubAppResource,\n    UrlDispatcher,\n)\n\n__all__ = (\"Application\", \"CleanupError\")\n\n\nif TYPE_CHECKING:\n    _AppSignal = Signal[Callable[[\"Application\"], Awaitable[None]]]\n    _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]\n    _Middlewares = FrozenList[Middleware]\n    _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]]\n    _Subapps = List[\"Application\"]\nelse:\n    # No type checker mode, skip types\n    _AppSignal = Signal\n    _RespPrepareSignal = Signal\n    _Middlewares = FrozenList\n    _MiddlewaresHandlers = Optional[Sequence]\n    _Subapps = List\n\n_T = TypeVar(\"_T\")\n_U = TypeVar(\"_U\")\n_Resource = TypeVar(\"_Resource\", bound=AbstractResource)\n\n\nclass Application(MutableMapping[Union[str, AppKey[Any]], Any]):\n    ATTRS = frozenset(\n        [\n            \"logger\",\n            \"_debug\",\n            \"_router\",\n            \"_loop\",\n            \"_handler_args\",\n            \"_middlewares\",\n            \"_middlewares_handlers\",\n            \"_run_middlewares\",\n            \"_state\",\n            \"_frozen\",\n            \"_pre_frozen\",\n            \"_subapps\",\n            \"_on_response_prepare\",\n            \"_on_startup\",\n            \"_on_shutdown\",\n            \"_on_cleanup\",\n            \"_client_max_size\",\n            \"_cleanup_ctx\",\n        ]\n    )\n\n    def __init__(\n        self,\n        *,\n        logger: logging.Logger = web_logger,\n        router: Optional[UrlDispatcher] = None,\n        middlewares: Iterable[Middleware] = (),\n        handler_args: Optional[Mapping[str, Any]] = None,\n        client_max_size: int = 1024**2,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        debug: Any = ...,  # mypy doesn't support ellipsis\n    ) -> None:\n        if router is None:\n            router = UrlDispatcher()\n        else:\n            warnings.warn(\n                \"router argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        assert isinstance(router, AbstractRouter), router\n\n        if loop is not None:\n            warnings.warn(\n                \"loop argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n\n        if debug is not ...:\n            warnings.warn(\n                \"debug argument is deprecated\", DeprecationWarning, stacklevel=2\n            )\n        self._debug = debug\n        self._router: UrlDispatcher = router\n        self._loop = loop\n        self._handler_args = handler_args\n        self.logger = logger\n\n        self._middlewares: _Middlewares = FrozenList(middlewares)\n\n        # initialized on freezing\n        self._middlewares_handlers: _MiddlewaresHandlers = None\n        # initialized on freezing\n        self._run_middlewares: Optional[bool] = None\n\n        self._state: Dict[Union[AppKey[Any], str], object] = {}\n        self._frozen = False\n        self._pre_frozen = False\n        self._subapps: _Subapps = []\n\n        self._on_response_prepare: _RespPrepareSignal = Signal(self)\n        self._on_startup: _AppSignal = Signal(self)\n        self._on_shutdown: _AppSignal = Signal(self)\n        self._on_cleanup: _AppSignal = Signal(self)\n        self._cleanup_ctx = CleanupContext()\n        self._on_startup.append(self._cleanup_ctx._on_startup)\n        self._on_cleanup.append(self._cleanup_ctx._on_cleanup)\n        self._client_max_size = client_max_size\n\n    def __init_subclass__(cls: Type[\"Application\"]) -> None:\n        warnings.warn(\n            \"Inheritance class {} from web.Application \"\n            \"is discouraged\".format(cls.__name__),\n            DeprecationWarning,\n            stacklevel=3,\n        )\n\n    if DEBUG:  # pragma: no cover\n\n        def __setattr__(self, name: str, val: Any) -> None:\n            if name not in self.ATTRS:\n                warnings.warn(\n                    \"Setting custom web.Application.{} attribute \"\n                    \"is discouraged\".format(name),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            super().__setattr__(name, val)\n\n    # MutableMapping API\n\n    def __eq__(self, other: object) -> bool:\n        return self is other\n\n    @overload  # type: ignore[override]\n    def __getitem__(self, key: AppKey[_T]) -> _T: ...\n\n    @overload\n    def __getitem__(self, key: str) -> Any: ...\n\n    def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:\n        return self._state[key]\n\n    def _check_frozen(self) -> None:\n        if self._frozen:\n            warnings.warn(\n                \"Changing state of started or joined \" \"application is deprecated\",\n                DeprecationWarning,\n                stacklevel=3,\n            )\n\n    @overload  # type: ignore[override]\n    def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...\n\n    @overload\n    def __setitem__(self, key: str, value: Any) -> None: ...\n\n    def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:\n        self._check_frozen()\n        if not isinstance(key, AppKey):\n            warnings.warn(\n                \"It is recommended to use web.AppKey instances for keys.\\n\"\n                + \"https://docs.aiohttp.org/en/stable/web_advanced.html\"\n                + \"#application-s-config\",\n                category=NotAppKeyWarning,\n                stacklevel=2,\n            )\n        self._state[key] = value\n\n    def __delitem__(self, key: Union[str, AppKey[_T]]) -> None:\n        self._check_frozen()\n        del self._state[key]\n\n    def __len__(self) -> int:\n        return len(self._state)\n\n    def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]:\n        return iter(self._state)\n\n    @overload  # type: ignore[override]\n    def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...\n\n    @overload\n    def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...\n\n    @overload\n    def get(self, key: str, default: Any = ...) -> Any: ...\n\n    def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:\n        return self._state.get(key, default)\n\n    ########\n    @property\n    def loop(self) -> asyncio.AbstractEventLoop:\n        # Technically the loop can be None\n        # but we mask it by explicit type cast\n        # to provide more convenient type annotation\n        warnings.warn(\"loop property is deprecated\", DeprecationWarning, stacklevel=2)\n        return cast(asyncio.AbstractEventLoop, self._loop)\n\n    def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        if self._loop is not None and self._loop is not loop:\n            raise RuntimeError(\n                \"web.Application instance initialized with different loop\"\n            )\n\n        self._loop = loop\n\n        # set loop debug\n        if self._debug is ...:\n            self._debug = loop.get_debug()\n\n        # set loop to sub applications\n        for subapp in self._subapps:\n            subapp._set_loop(loop)\n\n    @property\n    def pre_frozen(self) -> bool:\n        return self._pre_frozen\n\n    def pre_freeze(self) -> None:\n        if self._pre_frozen:\n            return\n\n        self._pre_frozen = True\n        self._middlewares.freeze()\n        self._router.freeze()\n        self._on_response_prepare.freeze()\n        self._cleanup_ctx.freeze()\n        self._on_startup.freeze()\n        self._on_shutdown.freeze()\n        self._on_cleanup.freeze()\n        self._middlewares_handlers = tuple(self._prepare_middleware())\n\n        # If current app and any subapp do not have middlewares avoid run all\n        # of the code footprint that it implies, which have a middleware\n        # hardcoded per app that sets up the current_app attribute. If no\n        # middlewares are configured the handler will receive the proper\n        # current_app without needing all of this code.\n        self._run_middlewares = True if self.middlewares else False\n\n        for subapp in self._subapps:\n            subapp.pre_freeze()\n            self._run_middlewares = self._run_middlewares or subapp._run_middlewares\n\n    @property\n    def frozen(self) -> bool:\n        return self._frozen\n\n    def freeze(self) -> None:\n        if self._frozen:\n            return\n\n        self.pre_freeze()\n        self._frozen = True\n        for subapp in self._subapps:\n            subapp.freeze()\n\n    @property\n    def debug(self) -> bool:\n        warnings.warn(\"debug property is deprecated\", DeprecationWarning, stacklevel=2)\n        return self._debug  # type: ignore[no-any-return]\n\n    def _reg_subapp_signals(self, subapp: \"Application\") -> None:\n        def reg_handler(signame: str) -> None:\n            subsig = getattr(subapp, signame)\n\n            async def handler(app: \"Application\") -> None:\n                await subsig.send(subapp)\n\n            appsig = getattr(self, signame)\n            appsig.append(handler)\n\n        reg_handler(\"on_startup\")\n        reg_handler(\"on_shutdown\")\n        reg_handler(\"on_cleanup\")\n\n    def add_subapp(self, prefix: str, subapp: \"Application\") -> PrefixedSubAppResource:\n        if not isinstance(prefix, str):\n            raise TypeError(\"Prefix must be str\")\n        prefix = prefix.rstrip(\"/\")\n        if not prefix:\n            raise ValueError(\"Prefix cannot be empty\")\n        factory = partial(PrefixedSubAppResource, prefix, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def _add_subapp(\n        self, resource_factory: Callable[[], _Resource], subapp: \"Application\"\n    ) -> _Resource:\n        if self.frozen:\n            raise RuntimeError(\"Cannot add sub application to frozen application\")\n        if subapp.frozen:\n            raise RuntimeError(\"Cannot add frozen application\")\n        resource = resource_factory()\n        self.router.register_resource(resource)\n        self._reg_subapp_signals(subapp)\n        self._subapps.append(subapp)\n        subapp.pre_freeze()\n        if self._loop is not None:\n            subapp._set_loop(self._loop)\n        return resource\n\n    def add_domain(self, domain: str, subapp: \"Application\") -> MatchedSubAppResource:\n        if not isinstance(domain, str):\n            raise TypeError(\"Domain must be str\")\n        elif \"*\" in domain:\n            rule: Domain = MaskDomain(domain)\n        else:\n            rule = Domain(domain)\n        factory = partial(MatchedSubAppResource, rule, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:\n        return self.router.add_routes(routes)\n\n    @property\n    def on_response_prepare(self) -> _RespPrepareSignal:\n        return self._on_response_prepare\n\n    @property\n    def on_startup(self) -> _AppSignal:\n        return self._on_startup\n\n    @property\n    def on_shutdown(self) -> _AppSignal:\n        return self._on_shutdown\n\n    @property\n    def on_cleanup(self) -> _AppSignal:\n        return self._on_cleanup\n\n    @property\n    def cleanup_ctx(self) -> \"CleanupContext\":\n        return self._cleanup_ctx\n\n    @property\n    def router(self) -> UrlDispatcher:\n        return self._router\n\n    @property\n    def middlewares(self) -> _Middlewares:\n        return self._middlewares\n\n    def _make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        if not issubclass(access_log_class, AbstractAccessLogger):\n            raise TypeError(\n                \"access_log_class must be subclass of \"\n                \"aiohttp.abc.AbstractAccessLogger, got {}\".format(access_log_class)\n            )\n\n        self._set_loop(loop)\n        self.freeze()\n\n        kwargs[\"debug\"] = self._debug\n        kwargs[\"access_log_class\"] = access_log_class\n        if self._handler_args:\n            for k, v in self._handler_args.items():\n                kwargs[k] = v\n\n        return Server(\n            self._handle,  # type: ignore[arg-type]\n            request_factory=self._make_request,\n            loop=self._loop,\n            **kwargs,\n        )\n\n    def make_handler(\n        self,\n        *,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        access_log_class: Type[AbstractAccessLogger] = AccessLogger,\n        **kwargs: Any,\n    ) -> Server:\n\n        warnings.warn(\n            \"Application.make_handler(...) is deprecated, \" \"use AppRunner API instead\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n\n        return self._make_handler(\n            loop=loop, access_log_class=access_log_class, **kwargs\n        )\n\n    async def startup(self) -> None:\n        \"\"\"Causes on_startup signal\n\n        Should be called in the event loop along with the request handler.\n        \"\"\"\n        await self.on_startup.send(self)\n\n    async def shutdown(self) -> None:\n        \"\"\"Causes on_shutdown signal\n\n        Should be called before cleanup()\n        \"\"\"\n        await self.on_shutdown.send(self)\n\n    async def cleanup(self) -> None:\n        \"\"\"Causes on_cleanup signal\n\n        Should be called after shutdown()\n        \"\"\"\n        if self.on_cleanup.frozen:\n            await self.on_cleanup.send(self)\n        else:\n            # If an exception occurs in startup, ensure cleanup contexts are completed.\n            await self._cleanup_ctx._on_cleanup(self)\n\n    def _make_request(\n        self,\n        message: RawRequestMessage,\n        payload: StreamReader,\n        protocol: RequestHandler,\n        writer: AbstractStreamWriter,\n        task: \"asyncio.Task[None]\",\n        _cls: Type[Request] = Request,\n    ) -> Request:\n        return _cls(\n            message,\n            payload,\n            protocol,\n            writer,\n            task,\n            self._loop,\n            client_max_size=self._client_max_size,\n        )\n\n    def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]:\n        for m in reversed(self._middlewares):\n            if getattr(m, \"__middleware_version__\", None) == 1:\n                yield m, True\n            else:\n                warnings.warn(\n                    'old-style middleware \"{!r}\" deprecated, ' \"see #2252\".format(m),\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n                yield m, False\n\n        yield _fix_request_current_app(self), True\n\n    async def _handle(self, request: Request) -> StreamResponse:\n        loop = asyncio.get_event_loop()\n        debug = loop.get_debug()\n        match_info = await self._router.resolve(request)\n        if debug:  # pragma: no cover\n            if not isinstance(match_info, AbstractMatchInfo):\n                raise TypeError(\n                    \"match_info should be AbstractMatchInfo \"\n                    \"instance, not {!r}\".format(match_info)\n                )\n        match_info.add_app(self)\n\n        match_info.freeze()\n\n        resp = None\n        request._match_info = match_info\n        expect = request.headers.get(hdrs.EXPECT)\n        if expect:\n            resp = await match_info.expect_handler(request)\n            await request.writer.drain()\n\n        if resp is None:\n            handler = match_info.handler\n\n            if self._run_middlewares:\n                for app in match_info.apps[::-1]:\n                    for m, new_style in app._middlewares_handlers:  # type: ignore[union-attr]\n                        if new_style:\n                            handler = update_wrapper(\n                                partial(m, handler=handler), handler  # type: ignore[misc]\n                            )\n                        else:\n                            handler = await m(app, handler)  # type: ignore[arg-type,assignment]\n\n            resp = await handler(request)\n\n        return resp\n\n    def __call__(self) -> \"Application\":\n        \"\"\"gunicorn compatibility\"\"\"\n        return self\n\n    def __repr__(self) -> str:\n        return f\"<Application 0x{id(self):x}>\"\n\n    def __bool__(self) -> bool:\n        return True\n\n\nclass CleanupError(RuntimeError):\n    @property\n    def exceptions(self) -> List[BaseException]:\n        return cast(List[BaseException], self.args[1])\n\n\nif TYPE_CHECKING:\n    _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]]\nelse:\n    _CleanupContextBase = FrozenList\n\n\nclass CleanupContext(_CleanupContextBase):\n    def __init__(self) -> None:\n        super().__init__()\n        self._exits: List[AsyncIterator[None]] = []\n\n    async def _on_startup(self, app: Application) -> None:\n        for cb in self:\n            it = cb(app).__aiter__()\n            await it.__anext__()\n            self._exits.append(it)\n\n    async def _on_cleanup(self, app: Application) -> None:\n        errors = []\n        for it in reversed(self._exits):\n            try:\n                await it.__anext__()\n            except StopAsyncIteration:\n                pass\n            except Exception as exc:\n                errors.append(exc)\n            else:\n                errors.append(RuntimeError(f\"{it!r} has more than one 'yield'\"))\n        if errors:\n            if len(errors) == 1:\n                raise errors[0]\n            else:\n                raise CleanupError(\"Multiple errors on cleanup stage\", errors)\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** CancelledError in Cleanup Context Prevents Completion of Other Cleanup Contexts

**Description:**
When using multiple cleanup contexts in an application, raising an `asyncio.CancelledError` within one cleanup context prevents the remaining cleanup contexts from completing. This leads to incomplete cleanup and potentially leaves resources improperly released.

**Example Code:**
```python
import asyncio
from aiohttp import web

async def cleanup_context_one(app: web.Application):
    print("Starting cleanup context one")
    yield
    print("Completed cleanup context one")

async def cleanup_context_two(app: web.Application):
    print("Starting cleanup context two")
    yield
    raise asyncio.CancelledError("Cleanup interrupted in context two")

async def cleanup_context_three(app: web.Application):
    print("Starting cleanup context three")
    yield
    print("Completed cleanup context three")

app = web.Application()
app.cleanup_ctx.append(cleanup_context_one)
app.cleanup_ctx.append(cleanup_context_two)
app.cleanup_ctx.append(cleanup_context_three)

async def run_app():
    await app.startup()
    await app.cleanup()

asyncio.run(run_app())
```

**Expected Behavior:**
All cleanup contexts should complete their execution regardless of exceptions raised in any individual context. Specifically, even if one cleanup context raises an `asyncio.CancelledError`, the other cleanup contexts should still run to completion.

**Actual Behavior:**
When a cleanup context raises an `asyncio.CancelledError`, it stops the execution of subsequent cleanup contexts. In the example above, "Completed cleanup context three" is never printed because the exception in `cleanup_context_two` halts the cleanup process.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### aiohttp/web_app.py
import asyncio
import logging
import warnings
from functools import partial, update_wrapper
from typing import (
    TYPE_CHECKING,
    Any,
    AsyncIterator,
    Awaitable,
    Callable,
    Dict,
    Iterable,
    Iterator,
    List,
    Mapping,
    MutableMapping,
    Optional,
    Sequence,
    Tuple,
    Type,
    TypeVar,
    Union,
    cast,
    overload,
)

from aiosignal import Signal
from frozenlist import FrozenList

from . import hdrs
from .abc import (
    AbstractAccessLogger,
    AbstractMatchInfo,
    AbstractRouter,
    AbstractStreamWriter,
)
from .helpers import DEBUG, AppKey
from .http_parser import RawRequestMessage
from .log import web_logger
from .streams import StreamReader
from .typedefs import Middleware
from .web_exceptions import NotAppKeyWarning
from .web_log import AccessLogger
from .web_middlewares import _fix_request_current_app
from .web_protocol import RequestHandler
from .web_request import Request
from .web_response import StreamResponse
from .web_routedef import AbstractRouteDef
from .web_server import Server
from .web_urldispatcher import (
    AbstractResource,
    AbstractRoute,
    Domain,
    MaskDomain,
    MatchedSubAppResource,
    PrefixedSubAppResource,
    UrlDispatcher,
)

__all__ = ("Application", "CleanupError")


if TYPE_CHECKING:
    _AppSignal = Signal[Callable[["Application"], Awaitable[None]]]
    _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]]
    _Middlewares = FrozenList[Middleware]
    _MiddlewaresHandlers = Optional[Sequence[Tuple[Middleware, bool]]]
    _Subapps = List["Application"]
else:
    # No type checker mode, skip types
    _AppSignal = Signal
    _RespPrepareSignal = Signal
    _Middlewares = FrozenList
    _MiddlewaresHandlers = Optional[Sequence]
    _Subapps = List

_T = TypeVar("_T")
_U = TypeVar("_U")
_Resource = TypeVar("_Resource", bound=AbstractResource)


class Application(MutableMapping[Union[str, AppKey[Any]], Any]):
    ATTRS = frozenset(
        [
            "logger",
            "_debug",
            "_router",
            "_loop",
            "_handler_args",
            "_middlewares",
            "_middlewares_handlers",
            "_run_middlewares",
            "_state",
            "_frozen",
            "_pre_frozen",
            "_subapps",
            "_on_response_prepare",
            "_on_startup",
            "_on_shutdown",
            "_on_cleanup",
            "_client_max_size",
            "_cleanup_ctx",
        ]
    )

    def __init__(
        self,
        *,
        logger: logging.Logger = web_logger,
        router: Optional[UrlDispatcher] = None,
        middlewares: Iterable[Middleware] = (),
        handler_args: Optional[Mapping[str, Any]] = None,
        client_max_size: int = 1024**2,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        debug: Any = ...,  # mypy doesn't support ellipsis
    ) -> None:
        if router is None:
            router = UrlDispatcher()
        else:
            warnings.warn(
                "router argument is deprecated", DeprecationWarning, stacklevel=2
            )
        assert isinstance(router, AbstractRouter), router

        if loop is not None:
            warnings.warn(
                "loop argument is deprecated", DeprecationWarning, stacklevel=2
            )

        if debug is not ...:
            warnings.warn(
                "debug argument is deprecated", DeprecationWarning, stacklevel=2
            )
        self._debug = debug
        self._router: UrlDispatcher = router
        self._loop = loop
        self._handler_args = handler_args
        self.logger = logger

        self._middlewares: _Middlewares = FrozenList(middlewares)

        # initialized on freezing
        self._middlewares_handlers: _MiddlewaresHandlers = None
        # initialized on freezing
        self._run_middlewares: Optional[bool] = None

        self._state: Dict[Union[AppKey[Any], str], object] = {}
        self._frozen = False
        self._pre_frozen = False
        self._subapps: _Subapps = []

        self._on_response_prepare: _RespPrepareSignal = Signal(self)
        self._on_startup: _AppSignal = Signal(self)
        self._on_shutdown: _AppSignal = Signal(self)
        self._on_cleanup: _AppSignal = Signal(self)
        self._cleanup_ctx = CleanupContext()
        self._on_startup.append(self._cleanup_ctx._on_startup)
        self._on_cleanup.append(self._cleanup_ctx._on_cleanup)
        self._client_max_size = client_max_size

    def __init_subclass__(cls: Type["Application"]) -> None:
        warnings.warn(
            "Inheritance class {} from web.Application "
            "is discouraged".format(cls.__name__),
            DeprecationWarning,
            stacklevel=3,
        )

    if DEBUG:  # pragma: no cover

        def __setattr__(self, name: str, val: Any) -> None:
            if name not in self.ATTRS:
                warnings.warn(
                    "Setting custom web.Application.{} attribute "
                    "is discouraged".format(name),
                    DeprecationWarning,
                    stacklevel=2,
                )
            super().__setattr__(name, val)

    # MutableMapping API

    def __eq__(self, other: object) -> bool:
        return self is other

    @overload  # type: ignore[override]
    def __getitem__(self, key: AppKey[_T]) -> _T: ...

    @overload
    def __getitem__(self, key: str) -> Any: ...

    def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:
        return self._state[key]

    def _check_frozen(self) -> None:
        if self._frozen:
            warnings.warn(
                "Changing state of started or joined " "application is deprecated",
                DeprecationWarning,
                stacklevel=3,
            )

    @overload  # type: ignore[override]
    def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...

    @overload
    def __setitem__(self, key: str, value: Any) -> None: ...

    def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:
        self._check_frozen()
        if not isinstance(key, AppKey):
            warnings.warn(
                "It is recommended to use web.AppKey instances for keys.\n"
                + "https://docs.aiohttp.org/en/stable/web_advanced.html"
                + "#application-s-config",
                category=NotAppKeyWarning,
                stacklevel=2,
            )
        self._state[key] = value

    def __delitem__(self, key: Union[str, AppKey[_T]]) -> None:
        self._check_frozen()
        del self._state[key]

    def __len__(self) -> int:
        return len(self._state)

    def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]:
        return iter(self._state)

    @overload  # type: ignore[override]
    def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...

    @overload
    def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...

    @overload
    def get(self, key: str, default: Any = ...) -> Any: ...

    def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:
        return self._state.get(key, default)

    ########
    @property
    def loop(self) -> asyncio.AbstractEventLoop:
        # Technically the loop can be None
        # but we mask it by explicit type cast
        # to provide more convenient type annotation
        warnings.warn("loop property is deprecated", DeprecationWarning, stacklevel=2)
        return cast(asyncio.AbstractEventLoop, self._loop)

    def _set_loop(self, loop: Optional[asyncio.AbstractEventLoop]) -> None:
        if loop is None:
            loop = asyncio.get_event_loop()
        if self._loop is not None and self._loop is not loop:
            raise RuntimeError(
                "web.Application instance initialized with different loop"
            )

        self._loop = loop

        # set loop debug
        if self._debug is ...:
            self._debug = loop.get_debug()

        # set loop to sub applications
        for subapp in self._subapps:
            subapp._set_loop(loop)

    @property
    def pre_frozen(self) -> bool:
        return self._pre_frozen

    def pre_freeze(self) -> None:
        if self._pre_frozen:
            return

        self._pre_frozen = True
        self._middlewares.freeze()
        self._router.freeze()
        self._on_response_prepare.freeze()
        self._cleanup_ctx.freeze()
        self._on_startup.freeze()
        self._on_shutdown.freeze()
        self._on_cleanup.freeze()
        self._middlewares_handlers = tuple(self._prepare_middleware())

        # If current app and any subapp do not have middlewares avoid run all
        # of the code footprint that it implies, which have a middleware
        # hardcoded per app that sets up the current_app attribute. If no
        # middlewares are configured the handler will receive the proper
        # current_app without needing all of this code.
        self._run_middlewares = True if self.middlewares else False

        for subapp in self._subapps:
            subapp.pre_freeze()
            self._run_middlewares = self._run_middlewares or subapp._run_middlewares

    @property
    def frozen(self) -> bool:
        return self._frozen

    def freeze(self) -> None:
        if self._frozen:
            return

        self.pre_freeze()
        self._frozen = True
        for subapp in self._subapps:
            subapp.freeze()

    @property
    def debug(self) -> bool:
        warnings.warn("debug property is deprecated", DeprecationWarning, stacklevel=2)
        return self._debug  # type: ignore[no-any-return]

    def _reg_subapp_signals(self, subapp: "Application") -> None:
        def reg_handler(signame: str) -> None:
            subsig = getattr(subapp, signame)

            async def handler(app: "Application") -> None:
                await subsig.send(subapp)

            appsig = getattr(self, signame)
            appsig.append(handler)

        reg_handler("on_startup")
        reg_handler("on_shutdown")
        reg_handler("on_cleanup")

    def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource:
        if not isinstance(prefix, str):
            raise TypeError("Prefix must be str")
        prefix = prefix.rstrip("/")
        if not prefix:
            raise ValueError("Prefix cannot be empty")
        factory = partial(PrefixedSubAppResource, prefix, subapp)
        return self._add_subapp(factory, subapp)

    def _add_subapp(
        self, resource_factory: Callable[[], _Resource], subapp: "Application"
    ) -> _Resource:
        if self.frozen:
            raise RuntimeError("Cannot add sub application to frozen application")
        if subapp.frozen:
            raise RuntimeError("Cannot add frozen application")
        resource = resource_factory()
        self.router.register_resource(resource)
        self._reg_subapp_signals(subapp)
        self._subapps.append(subapp)
        subapp.pre_freeze()
        if self._loop is not None:
            subapp._set_loop(self._loop)
        return resource

    def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource:
        if not isinstance(domain, str):
            raise TypeError("Domain must be str")
        elif "*" in domain:
            rule: Domain = MaskDomain(domain)
        else:
            rule = Domain(domain)
        factory = partial(MatchedSubAppResource, rule, subapp)
        return self._add_subapp(factory, subapp)

    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:
        return self.router.add_routes(routes)

    @property
    def on_response_prepare(self) -> _RespPrepareSignal:
        return self._on_response_prepare

    @property
    def on_startup(self) -> _AppSignal:
        return self._on_startup

    @property
    def on_shutdown(self) -> _AppSignal:
        return self._on_shutdown

    @property
    def on_cleanup(self) -> _AppSignal:
        return self._on_cleanup

    @property
    def cleanup_ctx(self) -> "CleanupContext":
        return self._cleanup_ctx

    @property
    def router(self) -> UrlDispatcher:
        return self._router

    @property
    def middlewares(self) -> _Middlewares:
        return self._middlewares

    def _make_handler(
        self,
        *,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        access_log_class: Type[AbstractAccessLogger] = AccessLogger,
        **kwargs: Any,
    ) -> Server:

        if not issubclass(access_log_class, AbstractAccessLogger):
            raise TypeError(
                "access_log_class must be subclass of "
                "aiohttp.abc.AbstractAccessLogger, got {}".format(access_log_class)
            )

        self._set_loop(loop)
        self.freeze()

        kwargs["debug"] = self._debug
        kwargs["access_log_class"] = access_log_class
        if self._handler_args:
            for k, v in self._handler_args.items():
                kwargs[k] = v

        return Server(
            self._handle,  # type: ignore[arg-type]
            request_factory=self._make_request,
            loop=self._loop,
            **kwargs,
        )

    def make_handler(
        self,
        *,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        access_log_class: Type[AbstractAccessLogger] = AccessLogger,
        **kwargs: Any,
    ) -> Server:

        warnings.warn(
            "Application.make_handler(...) is deprecated, " "use AppRunner API instead",
            DeprecationWarning,
            stacklevel=2,
        )

        return self._make_handler(
            loop=loop, access_log_class=access_log_class, **kwargs
        )

    async def startup(self) -> None:
        """Causes on_startup signal

        Should be called in the event loop along with the request handler.
        """
        await self.on_startup.send(self)

    async def shutdown(self) -> None:
        """Causes on_shutdown signal

        Should be called before cleanup()
        """
        await self.on_shutdown.send(self)

    async def cleanup(self) -> None:
        """Causes on_cleanup signal

        Should be called after shutdown()
        """
        if self.on_cleanup.frozen:
            await self.on_cleanup.send(self)
        else:
            # If an exception occurs in startup, ensure cleanup contexts are completed.
            await self._cleanup_ctx._on_cleanup(self)

    def _make_request(
        self,
        message: RawRequestMessage,
        payload: StreamReader,
        protocol: RequestHandler,
        writer: AbstractStreamWriter,
        task: "asyncio.Task[None]",
        _cls: Type[Request] = Request,
    ) -> Request:
        return _cls(
            message,
            payload,
            protocol,
            writer,
            task,
            self._loop,
            client_max_size=self._client_max_size,
        )

    def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]:
        for m in reversed(self._middlewares):
            if getattr(m, "__middleware_version__", None) == 1:
                yield m, True
            else:
                warnings.warn(
                    'old-style middleware "{!r}" deprecated, ' "see #2252".format(m),
                    DeprecationWarning,
                    stacklevel=2,
                )
                yield m, False

        yield _fix_request_current_app(self), True

    async def _handle(self, request: Request) -> StreamResponse:
        loop = asyncio.get_event_loop()
        debug = loop.get_debug()
        match_info = await self._router.resolve(request)
        if debug:  # pragma: no cover
            if not isinstance(match_info, AbstractMatchInfo):
                raise TypeError(
                    "match_info should be AbstractMatchInfo "
                    "instance, not {!r}".format(match_info)
                )
        match_info.add_app(self)

        match_info.freeze()

        resp = None
        request._match_info = match_info
        expect = request.headers.get(hdrs.EXPECT)
        if expect:
            resp = await match_info.expect_handler(request)
            await request.writer.drain()

        if resp is None:
            handler = match_info.handler

            if self._run_middlewares:
                for app in match_info.apps[::-1]:
                    for m, new_style in app._middlewares_handlers:  # type: ignore[union-attr]
                        if new_style:
                            handler = update_wrapper(
                                partial(m, handler=handler), handler  # type: ignore[misc]
                            )
                        else:
                            handler = await m(app, handler)  # type: ignore[arg-type,assignment]

            resp = await handler(request)

        return resp

    def __call__(self) -> "Application":
        """gunicorn compatibility"""
        return self

    def __repr__(self) -> str:
        return f"<Application 0x{id(self):x}>"

    def __bool__(self) -> bool:
        return True


class CleanupError(RuntimeError):
    @property
    def exceptions(self) -> List[BaseException]:
        return cast(List[BaseException], self.args[1])


if TYPE_CHECKING:
    _CleanupContextBase = FrozenList[Callable[[Application], AsyncIterator[None]]]
else:
    _CleanupContextBase = FrozenList


class CleanupContext(_CleanupContextBase):
    def __init__(self) -> None:
        super().__init__()
        self._exits: List[AsyncIterator[None]] = []

    async def _on_startup(self, app: Application) -> None:
        for cb in self:
            it = cb(app).__aiter__()
            await it.__anext__()
            self._exits.append(it)

    async def _on_cleanup(self, app: Application) -> None:
        errors = []
        for it in reversed(self._exits):
            try:
                await it.__anext__()
            except StopAsyncIteration:
                pass
            except Exception as exc:
                errors.append(exc)
            else:
                errors.append(RuntimeError(f"{it!r} has more than one 'yield'"))
        if errors:
            if len(errors) == 1:
                raise errors[0]
            else:
                raise CleanupError("Multiple errors on cleanup stage", errors)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user