{
    "file_diffs": [
        {
            "old_file_content": "",
            "new_file_content": "Fix `web.FileResponse` doing blocking I/O in the event loop\n",
            "header": {
                "file": {
                    "path": "CHANGES/8012.bugfix"
                },
                "misc_line": "new file mode 100644"
            },
            "index_line": {
                "old_commit_hash": "00000000",
                "new_commit_hash": "f5187075",
                "mode": ""
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "/dev/null"
            },
            "plus_file": {
                "path": "b/CHANGES/8012.bugfix"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 0,
                            "length": 0
                        },
                        "new_range": {
                            "start": 1,
                            "length": null
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Fix `web.FileResponse` doing blocking I/O in the event loop",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport mimetypes\nimport os\nimport pathlib\nfrom typing import (  # noqa\n    IO,\n    TYPE_CHECKING,\n    Any,\n    Awaitable,\n    Callable,\n    Final,\n    Iterator,\n    List,\n    Optional,\n    Tuple,\n    Union,\n    cast,\n)\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import ETAG_ANY, ETag, must_be_empty_body\nfrom .typedefs import LooseHeaders, PathLike\nfrom .web_exceptions import (\n    HTTPNotModified,\n    HTTPPartialContent,\n    HTTPPreconditionFailed,\n    HTTPRequestRangeNotSatisfiable,\n)\nfrom .web_response import StreamResponse\n\n__all__ = (\"FileResponse\",)\n\nif TYPE_CHECKING:\n    from .web_request import BaseRequest\n\n\n_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]\n\n\nNOSENDFILE: Final[bool] = bool(os.environ.get(\"AIOHTTP_NOSENDFILE\"))\n\n\nclass FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        filepath = self._path\n\n        gzip = False\n        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n\n            if gzip_path.is_file():\n                filepath = gzip_path\n                gzip = True\n\n        loop = asyncio.get_event_loop()\n        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))\n",
            "new_file_content": "import asyncio\nimport mimetypes\nimport os\nimport pathlib\nfrom typing import (  # noqa\n    IO,\n    TYPE_CHECKING,\n    Any,\n    Awaitable,\n    Callable,\n    Final,\n    Iterator,\n    List,\n    Optional,\n    Tuple,\n    Union,\n    cast,\n)\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import ETAG_ANY, ETag, must_be_empty_body\nfrom .typedefs import LooseHeaders, PathLike\nfrom .web_exceptions import (\n    HTTPNotModified,\n    HTTPPartialContent,\n    HTTPPreconditionFailed,\n    HTTPRequestRangeNotSatisfiable,\n)\nfrom .web_response import StreamResponse\n\n__all__ = (\"FileResponse\",)\n\nif TYPE_CHECKING:\n    from .web_request import BaseRequest\n\n\n_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]\n\n\nNOSENDFILE: Final[bool] = bool(os.environ.get(\"AIOHTTP_NOSENDFILE\"))\n\n\nclass FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    def _get_file_path_stat_and_gzip(\n        self, check_for_gzipped_file: bool\n    ) -> Tuple[pathlib.Path, os.stat_result, bool]:\n        \"\"\"Return the file path, stat result, and gzip status.\n\n        This method should be called from a thread executor\n        since it calls os.stat which may block.\n        \"\"\"\n        filepath = self._path\n        if check_for_gzipped_file:\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n            try:\n                return gzip_path, gzip_path.stat(), True\n            except OSError:\n                # Fall through and try the non-gzipped file\n                pass\n\n        return filepath, filepath.stat(), False\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        loop = asyncio.get_event_loop()\n        check_for_gzipped_file = \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\")\n        filepath, st, gzip = await loop.run_in_executor(\n            None, self._get_file_path_stat_and_gzip, check_for_gzipped_file\n        )\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))\n",
            "header": {
                "file": {
                    "path": "aiohttp/web_fileresponse.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "eb7a6a31",
                "new_commit_hash": "c3b38149",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/web_fileresponse.py"
            },
            "plus_file": {
                "path": "b/aiohttp/web_fileresponse.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 124,
                            "length": 19
                        },
                        "new_range": {
                            "start": 124,
                            "length": 31
                        },
                        "section": "class FileResponse(StreamResponse):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self.content_length = 0",
                                "type": "context"
                            },
                            {
                                "content": "        return await super().prepare(request)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:",
                                "type": "deleted"
                            },
                            {
                                "content": "    def _get_file_path_stat_and_gzip(",
                                "type": "added"
                            },
                            {
                                "content": "        self, check_for_gzipped_file: bool",
                                "type": "added"
                            },
                            {
                                "content": "    ) -> Tuple[pathlib.Path, os.stat_result, bool]:",
                                "type": "added"
                            },
                            {
                                "content": "        \"\"\"Return the file path, stat result, and gzip status.",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        This method should be called from a thread executor",
                                "type": "added"
                            },
                            {
                                "content": "        since it calls os.stat which may block.",
                                "type": "added"
                            },
                            {
                                "content": "        \"\"\"",
                                "type": "added"
                            },
                            {
                                "content": "        filepath = self._path",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        gzip = False",
                                "type": "deleted"
                            },
                            {
                                "content": "        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):",
                                "type": "deleted"
                            },
                            {
                                "content": "        if check_for_gzipped_file:",
                                "type": "added"
                            },
                            {
                                "content": "            gzip_path = filepath.with_name(filepath.name + \".gz\")",
                                "type": "context"
                            },
                            {
                                "content": "            try:",
                                "type": "added"
                            },
                            {
                                "content": "                return gzip_path, gzip_path.stat(), True",
                                "type": "added"
                            },
                            {
                                "content": "            except OSError:",
                                "type": "added"
                            },
                            {
                                "content": "                # Fall through and try the non-gzipped file",
                                "type": "added"
                            },
                            {
                                "content": "                pass",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "            if gzip_path.is_file():",
                                "type": "deleted"
                            },
                            {
                                "content": "                filepath = gzip_path",
                                "type": "deleted"
                            },
                            {
                                "content": "                gzip = True",
                                "type": "deleted"
                            },
                            {
                                "content": "        return filepath, filepath.stat(), False",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:",
                                "type": "added"
                            },
                            {
                                "content": "        loop = asyncio.get_event_loop()",
                                "type": "context"
                            },
                            {
                                "content": "        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)",
                                "type": "deleted"
                            },
                            {
                                "content": "        check_for_gzipped_file = \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\")",
                                "type": "added"
                            },
                            {
                                "content": "        filepath, st, gzip = await loop.run_in_executor(",
                                "type": "added"
                            },
                            {
                                "content": "            None, self._get_file_path_stat_and_gzip, check_for_gzipped_file",
                                "type": "added"
                            },
                            {
                                "content": "        )",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"",
                                "type": "context"
                            },
                            {
                                "content": "        last_modified = st.st_mtime",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web_fileresponse.py",
                            "type": "method",
                            "name": "FileResponse.prepare",
                            "content": "class FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        filepath = self._path\n\n        gzip = False\n        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n\n            if gzip_path.is_file():\n                filepath = gzip_path\n                gzip = True\n\n        loop = asyncio.get_event_loop()\n        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 127,
                            "end_lineno": 285,
                            "parent": {
                                "file_name": "aiohttp/web_fileresponse.py",
                                "type": "class",
                                "name": "FileResponse",
                                "content": "class FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        filepath = self._path\n\n        gzip = False\n        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n\n            if gzip_path.is_file():\n                filepath = gzip_path\n                gzip = True\n\n        loop = asyncio.get_event_loop()\n        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 44,
                                "end_lineno": 285,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "aiohttp/web_fileresponse.py",
                            "type": "class",
                            "name": "FileResponse",
                            "content": "class FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        filepath = self._path\n\n        gzip = False\n        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n\n            if gzip_path.is_file():\n                filepath = gzip_path\n                gzip = True\n\n        loop = asyncio.get_event_loop()\n        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 44,
                            "end_lineno": 285,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "aiohttp/web_fileresponse.py",
                            "type": "method",
                            "name": "FileResponse._get_file_path_stat_and_gzip",
                            "content": "class FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    def _get_file_path_stat_and_gzip(\n        self, check_for_gzipped_file: bool\n    ) -> Tuple[pathlib.Path, os.stat_result, bool]:\n        \"\"\"Return the file path, stat result, and gzip status.\n\n        This method should be called from a thread executor\n        since it calls os.stat which may block.\n        \"\"\"\n        filepath = self._path\n        if check_for_gzipped_file:\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n            try:\n                return gzip_path, gzip_path.stat(), True\n            except OSError:\n                # Fall through and try the non-gzipped file\n                pass\n\n        return filepath, filepath.stat(), False\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        loop = asyncio.get_event_loop()\n        check_for_gzipped_file = \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\")\n        filepath, st, gzip = await loop.run_in_executor(\n            None, self._get_file_path_stat_and_gzip, check_for_gzipped_file\n        )\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 127,
                            "end_lineno": 144,
                            "parent": {
                                "file_name": "aiohttp/web_fileresponse.py",
                                "type": "class",
                                "name": "FileResponse",
                                "content": "class FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    def _get_file_path_stat_and_gzip(\n        self, check_for_gzipped_file: bool\n    ) -> Tuple[pathlib.Path, os.stat_result, bool]:\n        \"\"\"Return the file path, stat result, and gzip status.\n\n        This method should be called from a thread executor\n        since it calls os.stat which may block.\n        \"\"\"\n        filepath = self._path\n        if check_for_gzipped_file:\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n            try:\n                return gzip_path, gzip_path.stat(), True\n            except OSError:\n                # Fall through and try the non-gzipped file\n                pass\n\n        return filepath, filepath.stat(), False\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        loop = asyncio.get_event_loop()\n        check_for_gzipped_file = \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\")\n        filepath, st, gzip = await loop.run_in_executor(\n            None, self._get_file_path_stat_and_gzip, check_for_gzipped_file\n        )\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 44,
                                "end_lineno": 297,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from pathlib import Path\nfrom unittest import mock\n\nfrom aiohttp import hdrs\nfrom aiohttp.test_utils import make_mocked_coro, make_mocked_request\nfrom aiohttp.web_fileresponse import FileResponse\n\n\ndef test_using_gzip_if_header_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = True\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert not filepath.open.called\n    assert gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = True\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = False\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = False\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_status_controlled_by_user(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath, status=203)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert file_sender._status == 203\n",
            "new_file_content": "from pathlib import Path\nfrom unittest import mock\n\nfrom aiohttp import hdrs\nfrom aiohttp.test_utils import make_mocked_coro, make_mocked_request\nfrom aiohttp.web_fileresponse import FileResponse\n\n\ndef test_using_gzip_if_header_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert not filepath.open.called\n    assert gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_status_controlled_by_user(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath, status=203)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert file_sender._status == 203\n",
            "header": {
                "file": {
                    "path": "tests/test_web_sendfile.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "0258def0",
                "new_commit_hash": "2817e085",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_web_sendfile.py"
            },
            "plus_file": {
                "path": "b/tests/test_web_sendfile.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 12,
                            "length": 7
                        },
                        "new_range": {
                            "start": 12,
                            "length": 6
                        },
                        "section": "def test_using_gzip_if_header_present_and_file_available(loop) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    )",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath.is_file.return_value = True",
                                "type": "deleted"
                            },
                            {
                                "content": "    gz_filepath.stat.return_value.st_size = 1024",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_sendfile.py",
                            "type": "function",
                            "name": "test_using_gzip_if_header_present_and_file_available",
                            "content": "def test_using_gzip_if_header_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = True\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert not filepath.open.called\n    assert gz_filepath.open.called",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 9,
                            "end_lineno": 30,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 34,
                            "length": 7
                        },
                        "new_range": {
                            "start": 33,
                            "length": 8
                        },
                        "section": "def test_gzip_if_header_not_present_and_file_available(loop) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath.is_file.return_value = True",
                                "type": "deleted"
                            },
                            {
                                "content": "    gz_filepath.stat.return_value.st_size = 1024",
                                "type": "added"
                            },
                            {
                                "content": "    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    filepath.name = \"logo.png\"",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_sendfile.py",
                            "type": "function",
                            "name": "test_gzip_if_header_not_present_and_file_available",
                            "content": "def test_gzip_if_header_not_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = True\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 33,
                            "end_lineno": 52,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 56,
                            "length": 7
                        },
                        "new_range": {
                            "start": 56,
                            "length": 7
                        },
                        "section": "def test_gzip_if_header_not_present_and_file_not_available(loop) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath.is_file.return_value = False",
                                "type": "deleted"
                            },
                            {
                                "content": "    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    filepath.name = \"logo.png\"",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_sendfile.py",
                            "type": "function",
                            "name": "test_gzip_if_header_not_present_and_file_not_available",
                            "content": "def test_gzip_if_header_not_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = False\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 55,
                            "end_lineno": 74,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 80,
                            "length": 7
                        },
                        "new_range": {
                            "start": 80,
                            "length": 7
                        },
                        "section": "def test_gzip_if_header_present_and_file_not_available(loop) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    )",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    gz_filepath.is_file.return_value = False",
                                "type": "deleted"
                            },
                            {
                                "content": "    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    filepath = mock.create_autospec(Path, spec_set=True)",
                                "type": "context"
                            },
                            {
                                "content": "    filepath.name = \"logo.png\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tests/test_web_sendfile.py",
                            "type": "function",
                            "name": "test_gzip_if_header_present_and_file_not_available",
                            "content": "def test_gzip_if_header_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.is_file.return_value = False\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 77,
                            "end_lineno": 98,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "fca5a7a0c8f3e713a4af2568fa94209f59f16fca^",
    "new_commit_hash": "fca5a7a0c8f3e713a4af2568fa94209f59f16fca",
    "commit_message": "[PR #8012/5f699bbb backport][3.9] Fix FileResponse doing blocking I/O in the event loop (#8015)\n\nCo-authored-by: J. Nick Koston <nick@koston.org>",
    "commit_date": "2024-01-10T14:56:40-10:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "fca5a7a0c8f3e713a4af2568fa94209f59f16fca",
    "test_file_codes": [
        "from pathlib import Path\nfrom unittest import mock\n\nfrom aiohttp import hdrs\nfrom aiohttp.test_utils import make_mocked_coro, make_mocked_request\nfrom aiohttp.web_fileresponse import FileResponse\n\n\ndef test_using_gzip_if_header_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert not filepath.open.called\n    assert gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.return_value.st_size = 1024\n    gz_filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_not_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_gzip_if_header_present_and_file_not_available(loop) -> None:\n    request = make_mocked_request(\n        \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n    )\n\n    gz_filepath = mock.create_autospec(Path, spec_set=True)\n    gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.with_name.return_value = gz_filepath\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert filepath.open.called\n    assert not gz_filepath.open.called\n\n\ndef test_status_controlled_by_user(loop) -> None:\n    request = make_mocked_request(\"GET\", \"http://python.org/logo.png\", headers={})\n\n    filepath = mock.create_autospec(Path, spec_set=True)\n    filepath.name = \"logo.png\"\n    filepath.stat.return_value.st_size = 1024\n    filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n\n    file_sender = FileResponse(filepath, status=203)\n    file_sender._path = filepath\n    file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n\n    loop.run_until_complete(file_sender.prepare(request))\n\n    assert file_sender._status == 203\n",
        "import asyncio\nimport os\nimport socket\nimport ssl\nimport sys\nfrom hashlib import md5, sha256\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom uuid import uuid4\n\nimport pytest\n\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 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"
    ],
    "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_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/requirements/cython.txt\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/hdrs.py\n./tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_helpers.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_http_parser.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_http_writer.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_websocket.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_helpers.pyi\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/_cparser.pxd\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/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 install\n\n> llhttp@9.1.3 prepare\n> npm run clean && npm run build-ts\n\n\n> llhttp@9.1.3 clean\n> rm -rf lib && rm -rf test/tmp\n\n\n> llhttp@9.1.3 build-ts\n> tsc\n\n\nadded 157 packages, and audited 158 packages in 4s\n\n23 packages are looking for funding\n  run `npm fund` for details\n\n5 vulnerabilities (4 moderate, 1 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_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/vendor/llhttp'\nnpx ts-node bin/generate.ts\nmake[1]: Leaving directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/vendor/llhttp'\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/__init__.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/abc.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/base_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/client.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/client_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/client_proto.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/client_reqrep.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/client_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/compression_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/connector.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/cookiejar.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/formdata.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/http.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/http_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/http_parser.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/http_websocket.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/http_writer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/locks.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/multipart.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/payload.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/payload_streamer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/pytest_plugin.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/resolver.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/streams.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/tcp_helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/test_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/tracing.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/typedefs.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_app.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_fileresponse.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_middlewares.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_request.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_response.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_routedef.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_runner.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_server.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_urldispatcher.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/aiohttp/web_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/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 67ms\nPrepared 1 package in 0.28ms\nInstalled 1 package in 17ms\n + pip==24.3.1\nResolved 3 packages in 15ms\nInstalled 3 packages in 13ms\n + cython==3.0.5\n + multidict==6.0.4\n + typing-extensions==4.7.1\nResolved 95 packages in 50ms\nUninstalled 1 package in 57ms\nInstalled 93 packages in 97ms\n + aiodns==3.1.1\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.1.0\n + babel==2.12.1\n + blockdiag==3.0.0\n + brotli==1.1.0\n + build==0.10.0\n + certifi==2023.7.22\n + cffi==1.15.1\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.3.2\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.3.0\n + frozenlist==1.4.0\n + funcparserlib==1.0.1\n + gidgethub==5.3.0\n + gunicorn==21.2.0\n + identify==2.5.26\n + idna==3.4\n + imagesize==1.4.1\n + importlib-metadata==8.5.0\n + importlib-resources==6.4.5\n + incremental==22.10.0\n + iniconfig==2.0.0\n + jinja2==3.1.2\n + markupsafe==2.1.3\n + mypy==1.7.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.3.0\n + platformdirs==3.10.0\n + pluggy==1.2.0\n + pre-commit==3.5.0\n + proxy-py==2.4.4rc4\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==7.4.3\n + pytest-cov==4.1.0\n + pytest-mock==3.12.0\n + python-dateutil==2.8.2\n + python-on-whales==0.67.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.17.1\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 + 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.3\n + zipp==3.21.0\nResolved 8 packages in 1.22s\nPrepared 1 package in 15.96s\nInstalled 1 package in 0.87ms\n + aiohttp==3.9.1.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca)\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.23.8\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-7.4.3, pluggy-1.2.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca\nconfigfile: setup.cfg\nplugins: mock-3.12.0, asyncio-0.23.8, cov-4.1.0\nasyncio: mode=strict\ncollecting ... collected 5 items\n\nr2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop] PASSED [ 20%]\nr2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop] PASSED [ 40%]\nr2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop] PASSED [ 60%]\nr2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop] PASSED [ 80%]\nr2e_tests/test_1.py::test_status_controlled_by_user[pyloop] 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                              90      8      0      0    91%\naiohttp/base_protocol.py                    66     51     22      0    17%\naiohttp/client.py                          489    368    178      2    19%\naiohttp/client_exceptions.py               135     61     16      0    49%\naiohttp/client_proto.py                    149    120     54      0    14%\naiohttp/client_reqrep.py                   680    507    290      0    18%\naiohttp/client_ws.py                       210    168     66      1    16%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       715    591    311      0    12%\naiohttp/cookiejar.py                       259    205    125      0    14%\naiohttp/formdata.py                         93     76     46      0    12%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         519    315    178      7    32%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     497    398    200      1    14%\naiohttp/http_websocket.py                  388    286    152      0    19%\naiohttp/http_writer.py                     118     81     50      1    23%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       562    455    232      0    14%\naiohttp/payload.py                         219    107     56      1    43%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   167     96     42      6    38%\naiohttp/resolver.py                         68     48     22      0    22%\naiohttp/streams.py                         396    313    144      0    15%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      313    138     52     11    53%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             135     95     54      0    22%\naiohttp/web_app.py                         302    196     74      0    28%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                144     62     48     13    53%\naiohttp/web_log.py                         102     58     20      0    36%\naiohttp/web_middlewares.py                  55     42     20      0    17%\naiohttp/web_protocol.py                    343    283    157      0    12%\naiohttp/web_request.py                     435    241    128      8    37%\naiohttp/web_response.py                    483    323    218     12    25%\naiohttp/web_routedef.py                    106     46     10      0    55%\naiohttp/web_runner.py                      215    132     38      0    33%\naiohttp/web_server.py                       41     22      8      0    39%\naiohttp/web_urldispatcher.py               708    442    161      2    31%\naiohttp/web_ws.py                          331    265    116      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          105    105     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            183    183     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2340   2340     76      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               787    787     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               482    482     23      0     0%\ntests/test_client_ws.py                    415    415      2      0     0%\ntests/test_client_ws_functional.py         606    606     16      0     0%\ntests/test_connector.py                   1520   1520     76      0     0%\ntests/test_cookiejar.py                    349    349     22      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      72     72      6      0     0%\ntests/test_helpers.py                      464    464     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                  963    963     50      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       31     31      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                    747    747     22      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       94     94      0      0     0%\ntests/test_proxy.py                        285    285      0      0     0%\ntests/test_proxy_functional.py             468    468     20      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     176    176     12      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      522    522     30      0     0%\ntests/test_streams.py                     1077   1077     40      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   242    242      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  844    844     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              1547   1547     36      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           40     40      0      0     0%\ntests/test_web_response.py                 863    863      4      0     0%\ntests/test_web_runner.py                   146    146      4      0     0%\ntests/test_web_sendfile.py                  76     76      0      0     0%\ntests/test_web_sendfile_functional.py      634    634     18      0     0%\ntests/test_web_server.py                   163    163      0      0     0%\ntests/test_web_urldispatcher.py            306    306     14      0     0%\ntests/test_web_websocket.py                334    334      8      0     0%\ntests/test_web_websocket_functional.py     542    542     16      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                                    32326  28891   4122     66    10%\n\n============================= slowest 10 durations =============================\n0.13s call     r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\n0.06s call     r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\nPASSED r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\nPASSED r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\nPASSED r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\nPASSED r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\n============================== 5 passed in 3.94s ===============================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-7.4.3, pluggy-1.2.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_fca5a7a0c8f3e713a4af2568fa94209f59f16fca\nconfigfile: setup.cfg\nplugins: mock-3.12.0, asyncio-0.23.8, cov-4.1.0\nasyncio: mode=strict\ncollecting ... collected 5 items\n\nr2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop] PASSED [ 20%]\nr2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop] PASSED [ 40%]\nr2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop] PASSED [ 60%]\nr2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop] FAILED [ 80%]\nr2e_tests/test_1.py::test_status_controlled_by_user[pyloop] PASSED       [100%]\n\n=================================== FAILURES ===================================\n__________ test_gzip_if_header_present_and_file_not_available[pyloop] __________\n\nloop = <_UnixSelectorEventLoop running=False closed=False debug=False>\n\n    def test_gzip_if_header_present_and_file_not_available(loop) -> None:\n        request = make_mocked_request(\n            \"GET\", \"http://python.org/logo.png\", headers={hdrs.ACCEPT_ENCODING: \"gzip\"}\n        )\n    \n        gz_filepath = mock.create_autospec(Path, spec_set=True)\n        gz_filepath.stat.side_effect = OSError(2, \"No such file or directory\")\n    \n        filepath = mock.create_autospec(Path, spec_set=True)\n        filepath.name = \"logo.png\"\n        filepath.with_name.return_value = gz_filepath\n        filepath.stat.return_value.st_size = 1024\n        filepath.stat.return_value.st_mtime_ns = 1603733507222449291\n    \n        file_sender = FileResponse(filepath)\n        file_sender._path = filepath\n        file_sender._sendfile = make_mocked_coro(None)  # type: ignore[assignment]\n    \n>       loop.run_until_complete(file_sender.prepare(request))\n\nfile_sender = <FileResponse OK not prepared>\nfilepath   = <MagicMock spec_set='Path' id='128358543425440'>\ngz_filepath = <MagicMock name='mock.with_name()' spec_set='Path' id='128358543421696'>\nloop       = <_UnixSelectorEventLoop running=False closed=False debug=False>\nrequest    = <Request GET /logo.png >\n\nr2e_tests/test_1.py:95: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/asyncio/base_events.py:647: in run_until_complete\n    return future.result()\n        future     = <Task finished name='Task-4' coro=<FileResponse.prepare() done, defined at /home/gcpuser/buckets/local_repoeval_bucket...3a4af2568fa94209f59f16fca/aiohttp/web_fileresponse.py:127> exception=FileNotFoundError(2, 'No such file or directory')>\n        new_task   = True\n        self       = <_UnixSelectorEventLoop running=False closed=False debug=False>\naiohttp/web_fileresponse.py:139: in prepare\n    st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n        __class__  = <class 'aiohttp.web_fileresponse.FileResponse'>\n        filepath   = <MagicMock name='mock.with_name()' spec_set='Path' id='128358543421696'>\n        gzip       = True\n        gzip_path  = <MagicMock name='mock.with_name()' spec_set='Path' id='128358543421696'>\n        loop       = <_UnixSelectorEventLoop running=False closed=False debug=False>\n        request    = <Request GET /logo.png >\n        self       = <FileResponse OK not prepared>\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/concurrent/futures/thread.py:58: in run\n    result = self.fn(*self.args, **self.kwargs)\n        self       = None\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/mock.py:1092: in __call__\n    return self._mock_call(*args, **kwargs)\n        args       = ()\n        kwargs     = {}\n        self       = <MagicMock name='mock.with_name().stat' spec_set='function' id='128358542855760'>\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/mock.py:1096: in _mock_call\n    return self._execute_mock_call(*args, **kwargs)\n        args       = ()\n        kwargs     = {}\n        self       = <MagicMock name='mock.with_name().stat' spec_set='function' id='128358542855760'>\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <MagicMock name='mock.with_name().stat' spec_set='function' id='128358542855760'>\nargs = (), kwargs = {}\neffect = FileNotFoundError(2, 'No such file or directory')\n\n    def _execute_mock_call(self, /, *args, **kwargs):\n        # separate from _increment_mock_call so that awaited functions are\n        # executed separately from their call, also AsyncMock overrides this method\n    \n        effect = self.side_effect\n        if effect is not None:\n            if _is_exception(effect):\n>               raise effect\nE               FileNotFoundError: [Errno 2] No such file or directory\n\nargs       = ()\neffect     = FileNotFoundError(2, 'No such file or directory')\nkwargs     = {}\nself       = <MagicMock name='mock.with_name().stat' spec_set='function' id='128358542855760'>\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/mock.py:1151: FileNotFoundError\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                              90      8      0      0    91%\naiohttp/base_protocol.py                    66     51     22      0    17%\naiohttp/client.py                          489    368    178      2    19%\naiohttp/client_exceptions.py               135     61     16      0    49%\naiohttp/client_proto.py                    149    120     54      0    14%\naiohttp/client_reqrep.py                   680    507    290      0    18%\naiohttp/client_ws.py                       210    168     66      1    16%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       715    591    311      0    12%\naiohttp/cookiejar.py                       259    205    125      0    14%\naiohttp/formdata.py                         93     76     46      0    12%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         519    315    178      7    32%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     497    398    200      1    14%\naiohttp/http_websocket.py                  388    286    152      0    19%\naiohttp/http_writer.py                     118     81     50      1    23%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       562    455    232      0    14%\naiohttp/payload.py                         219    107     56      1    43%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   167     96     42      6    38%\naiohttp/resolver.py                         68     48     22      0    22%\naiohttp/streams.py                         396    313    144      0    15%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      313    138     52     11    53%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             135     95     54      0    22%\naiohttp/web_app.py                         302    196     74      0    28%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                141     62     50     14    52%\naiohttp/web_log.py                         102     58     20      0    36%\naiohttp/web_middlewares.py                  55     42     20      0    17%\naiohttp/web_protocol.py                    343    283    157      0    12%\naiohttp/web_request.py                     435    238    128      8    37%\naiohttp/web_response.py                    483    318    218     14    26%\naiohttp/web_routedef.py                    106     46     10      0    55%\naiohttp/web_runner.py                      215    132     38      0    33%\naiohttp/web_server.py                       41     22      8      0    39%\naiohttp/web_urldispatcher.py               708    442    161      2    31%\naiohttp/web_ws.py                          331    265    116      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          105    105     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            183    183     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2340   2340     76      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               787    787     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               482    482     23      0     0%\ntests/test_client_ws.py                    415    415      2      0     0%\ntests/test_client_ws_functional.py         606    606     16      0     0%\ntests/test_connector.py                   1520   1520     76      0     0%\ntests/test_cookiejar.py                    349    349     22      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      72     72      6      0     0%\ntests/test_helpers.py                      464    464     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                  963    963     50      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       31     31      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                    747    747     22      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       94     94      0      0     0%\ntests/test_proxy.py                        285    285      0      0     0%\ntests/test_proxy_functional.py             468    468     20      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     176    176     12      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      522    522     30      0     0%\ntests/test_streams.py                     1077   1077     40      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   242    242      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  844    844     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              1547   1547     36      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           40     40      0      0     0%\ntests/test_web_response.py                 863    863      4      0     0%\ntests/test_web_runner.py                   146    146      4      0     0%\ntests/test_web_sendfile.py                  76     76      0      0     0%\ntests/test_web_sendfile_functional.py      634    634     18      0     0%\ntests/test_web_server.py                   163    163      0      0     0%\ntests/test_web_urldispatcher.py            306    306     14      0     0%\ntests/test_web_websocket.py                334    334      8      0     0%\ntests/test_web_websocket_functional.py     542    542     16      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                                    32323  28883   4124     69    10%\n\n============================= slowest 10 durations =============================\n0.13s call     r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\n0.11s call     r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\n0.06s call     r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_using_gzip_if_header_present_and_file_available[pyloop]\nPASSED r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_available[pyloop]\nPASSED r2e_tests/test_1.py::test_gzip_if_header_not_present_and_file_not_available[pyloop]\nPASSED r2e_tests/test_1.py::test_status_controlled_by_user[pyloop]\nFAILED r2e_tests/test_1.py::test_gzip_if_header_present_and_file_not_available[pyloop]\n========================= 1 failed, 4 passed in 4.21s ==========================\n",
    "old_commit_res_stderr": ""
}
CHANGES/8012.bugfix aiohttp/web_fileresponse.py tests/test_web_sendfile.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** FileResponse Raises FileNotFoundError When Gzip Encoding is Requested but File is Missing

**Description:**
When a client includes `"gzip"` in the `Accept-Encoding` header, and the server does not have the corresponding gzipped file available, the `FileResponse` performs blocking file operations within the event loop. This results in the event loop being blocked and a `FileNotFoundError` being raised, causing the server to fail to respond properly.

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

async def handler(request):
    return web.FileResponse('logo.png.gz')  # Gzipped file does not exist

app = web.Application()
app.router.add_get('/logo.png', handler)

# Simulate a request with gzip encoding without the gzipped file available
async def test_prepare():
    request = make_mocked_request('GET', '/logo.png', headers={'Accept-Encoding': 'gzip'})
    file_response = FileResponse(Path('logo.png.gz'))
    file_response._path = Path('logo.png.gz')
    file_response._sendfile = make_mocked_coro(None)
    await file_response.prepare(request)
    # Expected: Gracefully handle missing gzip file
    # Actual: Raises FileNotFoundError due to blocking I/O
```

**Expected Behavior:**
When a gzipped file is requested via the `Accept-Encoding` header but the file does not exist, `FileResponse` should handle the situation gracefully without blocking the event loop or raising an uncaught exception. It should either serve the non-gzipped version of the file or respond with an appropriate error message.

**Actual Behavior:**
The `FileResponse` performs blocking I/O operations within the event loop, leading to the event loop being blocked and a `FileNotFoundError` being raised when the gzipped file is not found. This results in failed responses instead of handling the missing gzipped file gracefully.

[/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_fileresponse.py
import asyncio
import mimetypes
import os
import pathlib
from typing import (  # noqa
    IO,
    TYPE_CHECKING,
    Any,
    Awaitable,
    Callable,
    Final,
    Iterator,
    List,
    Optional,
    Tuple,
    Union,
    cast,
)

from . import hdrs
from .abc import AbstractStreamWriter
from .helpers import ETAG_ANY, ETag, must_be_empty_body
from .typedefs import LooseHeaders, PathLike
from .web_exceptions import (
    HTTPNotModified,
    HTTPPartialContent,
    HTTPPreconditionFailed,
    HTTPRequestRangeNotSatisfiable,
)
from .web_response import StreamResponse

__all__ = ("FileResponse",)

if TYPE_CHECKING:
    from .web_request import BaseRequest


_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]


NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE"))


class FileResponse(StreamResponse):
    """A response object can be used to send files."""

    def __init__(
        self,
        path: PathLike,
        chunk_size: int = 256 * 1024,
        status: int = 200,
        reason: Optional[str] = None,
        headers: Optional[LooseHeaders] = None,
    ) -> None:
        super().__init__(status=status, reason=reason, headers=headers)

        self._path = pathlib.Path(path)
        self._chunk_size = chunk_size

    async def _sendfile_fallback(
        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int
    ) -> AbstractStreamWriter:
        # To keep memory usage low,fobj is transferred in chunks
        # controlled by the constructor's chunk_size argument.

        chunk_size = self._chunk_size
        loop = asyncio.get_event_loop()

        await loop.run_in_executor(None, fobj.seek, offset)

        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)
        while chunk:
            await writer.write(chunk)
            count = count - chunk_size
            if count <= 0:
                break
            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))

        await writer.drain()
        return writer

    async def _sendfile(
        self, request: "BaseRequest", fobj: IO[Any], offset: int, count: int
    ) -> AbstractStreamWriter:
        writer = await super().prepare(request)
        assert writer is not None

        if NOSENDFILE or self.compression:
            return await self._sendfile_fallback(writer, fobj, offset, count)

        loop = request._loop
        transport = request.transport
        assert transport is not None

        try:
            await loop.sendfile(transport, fobj, offset, count)
        except NotImplementedError:
            return await self._sendfile_fallback(writer, fobj, offset, count)

        await super().write_eof()
        return writer

    @staticmethod
    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:
        if len(etags) == 1 and etags[0].value == ETAG_ANY:
            return True
        return any(etag.value == etag_value for etag in etags if not etag.is_weak)

    async def _not_modified(
        self, request: "BaseRequest", etag_value: str, last_modified: float
    ) -> Optional[AbstractStreamWriter]:
        self.set_status(HTTPNotModified.status_code)
        self._length_check = False
        self.etag = etag_value  # type: ignore[assignment]
        self.last_modified = last_modified  # type: ignore[assignment]
        # Delete any Content-Length headers provided by user. HTTP 304
        # should always have empty response body
        return await super().prepare(request)

    async def _precondition_failed(
        self, request: "BaseRequest"
    ) -> Optional[AbstractStreamWriter]:
        self.set_status(HTTPPreconditionFailed.status_code)
        self.content_length = 0
        return await super().prepare(request)

    async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]:
        filepath = self._path

        gzip = False
        if "gzip" in request.headers.get(hdrs.ACCEPT_ENCODING, ""):
            gzip_path = filepath.with_name(filepath.name + ".gz")

            if gzip_path.is_file():
                filepath = gzip_path
                gzip = True

        loop = asyncio.get_event_loop()
        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)

        etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}"
        last_modified = st.st_mtime

        # https://tools.ietf.org/html/rfc7232#section-6
        ifmatch = request.if_match
        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):
            return await self._precondition_failed(request)

        unmodsince = request.if_unmodified_since
        if (
            unmodsince is not None
            and ifmatch is None
            and st.st_mtime > unmodsince.timestamp()
        ):
            return await self._precondition_failed(request)

        ifnonematch = request.if_none_match
        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):
            return await self._not_modified(request, etag_value, last_modified)

        modsince = request.if_modified_since
        if (
            modsince is not None
            and ifnonematch is None
            and st.st_mtime <= modsince.timestamp()
        ):
            return await self._not_modified(request, etag_value, last_modified)

        if hdrs.CONTENT_TYPE not in self.headers:
            ct, encoding = mimetypes.guess_type(str(filepath))
            if not ct:
                ct = "application/octet-stream"
            should_set_ct = True
        else:
            encoding = "gzip" if gzip else None
            should_set_ct = False

        status = self._status
        file_size = st.st_size
        count = file_size

        start = None

        ifrange = request.if_range
        if ifrange is None or st.st_mtime <= ifrange.timestamp():
            # If-Range header check:
            # condition = cached date >= last modification date
            # return 206 if True else 200.
            # if False:
            #   Range header would not be processed, return 200
            # if True but Range header missing
            #   return 200
            try:
                rng = request.http_range
                start = rng.start
                end = rng.stop
            except ValueError:
                # https://tools.ietf.org/html/rfc7233:
                # A server generating a 416 (Range Not Satisfiable) response to
                # a byte-range request SHOULD send a Content-Range header field
                # with an unsatisfied-range value.
                # The complete-length in a 416 response indicates the current
                # length of the selected representation.
                #
                # Will do the same below. Many servers ignore this and do not
                # send a Content-Range header with HTTP 416
                self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}"
                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)
                return await super().prepare(request)

            # If a range request has been made, convert start, end slice
            # notation into file pointer offset and count
            if start is not None or end is not None:
                if start < 0 and end is None:  # return tail of file
                    start += file_size
                    if start < 0:
                        # if Range:bytes=-1000 in request header but file size
                        # is only 200, there would be trouble without this
                        start = 0
                    count = file_size - start
                else:
                    # rfc7233:If the last-byte-pos value is
                    # absent, or if the value is greater than or equal to
                    # the current length of the representation data,
                    # the byte range is interpreted as the remainder
                    # of the representation (i.e., the server replaces the
                    # value of last-byte-pos with a value that is one less than
                    # the current length of the selected representation).
                    count = (
                        min(end if end is not None else file_size, file_size) - start
                    )

                if start >= file_size:
                    # HTTP 416 should be returned in this case.
                    #
                    # According to https://tools.ietf.org/html/rfc7233:
                    # If a valid byte-range-set includes at least one
                    # byte-range-spec with a first-byte-pos that is less than
                    # the current length of the representation, or at least one
                    # suffix-byte-range-spec with a non-zero suffix-length,
                    # then the byte-range-set is satisfiable. Otherwise, the
                    # byte-range-set is unsatisfiable.
                    self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}"
                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)
                    return await super().prepare(request)

                status = HTTPPartialContent.status_code
                # Even though you are sending the whole file, you should still
                # return a HTTP 206 for a Range request.
                self.set_status(status)

        if should_set_ct:
            self.content_type = ct  # type: ignore[assignment]
        if encoding:
            self.headers[hdrs.CONTENT_ENCODING] = encoding
        if gzip:
            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING

        self.etag = etag_value  # type: ignore[assignment]
        self.last_modified = st.st_mtime  # type: ignore[assignment]
        self.content_length = count

        self.headers[hdrs.ACCEPT_RANGES] = "bytes"

        real_start = cast(int, start)

        if status == HTTPPartialContent.status_code:
            self.headers[hdrs.CONTENT_RANGE] = "bytes {}-{}/{}".format(
                real_start, real_start + count - 1, file_size
            )

        # If we are sending 0 bytes calling sendfile() will throw a ValueError
        if count == 0 or must_be_empty_body(request.method, self.status):
            return await super().prepare(request)

        fobj = await loop.run_in_executor(None, filepath.open, "rb")
        if start:  # be aware that start could be None or int=0 here.
            offset = start
        else:
            offset = 0

        try:
            return await self._sendfile(request, fobj, offset, count)
        finally:
            await asyncio.shield(loop.run_in_executor(None, fobj.close))

```
--- 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:** FileResponse Raises FileNotFoundError When Gzip Encoding is Requested but File is Missing

**Description:**
When a client includes `"gzip"` in the `Accept-Encoding` header, and the server does not have the corresponding gzipped file available, the `FileResponse` performs blocking file operations within the event loop. This results in the event loop being blocked and a `FileNotFoundError` being raised, causing the server to fail to respond properly.

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

async def handler(request):
    return web.FileResponse('logo.png.gz')  # Gzipped file does not exist

app = web.Application()
app.router.add_get('/logo.png', handler)

# Simulate a request with gzip encoding without the gzipped file available
async def test_prepare():
    request = make_mocked_request('GET', '/logo.png', headers={'Accept-Encoding': 'gzip'})
    file_response = FileResponse(Path('logo.png.gz'))
    file_response._path = Path('logo.png.gz')
    file_response._sendfile = make_mocked_coro(None)
    await file_response.prepare(request)
    # Expected: Gracefully handle missing gzip file
    # Actual: Raises FileNotFoundError due to blocking I/O
```

**Expected Behavior:**
When a gzipped file is requested via the `Accept-Encoding` header but the file does not exist, `FileResponse` should handle the situation gracefully without blocking the event loop or raising an uncaught exception. It should either serve the non-gzipped version of the file or respond with an appropriate error message.

**Actual Behavior:**
The `FileResponse` performs blocking I/O operations within the event loop, leading to the event loop being blocked and a `FileNotFoundError` being raised when the gzipped file is not found. This results in failed responses instead of handling the missing gzipped file gracefully.

[/ISSUE]
{
    "test_using_gzip_if_header_present_and_file_available[pyloop]": "PASSED",
    "test_gzip_if_header_not_present_and_file_available[pyloop]": "PASSED",
    "test_gzip_if_header_not_present_and_file_not_available[pyloop]": "PASSED",
    "test_gzip_if_header_present_and_file_not_available[pyloop]": "PASSED",
    "test_status_controlled_by_user[pyloop]": "PASSED"
}
diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py
index eb7a6a31..c3b38149 100644
--- a/aiohttp/web_fileresponse.py
+++ b/aiohttp/web_fileresponse.py
@@ -124,19 +124,31 @@ class FileResponse(StreamResponse):
         self.content_length = 0
         return await super().prepare(request)
 
-    async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]:
+    def _get_file_path_stat_and_gzip(
+        self, check_for_gzipped_file: bool
+    ) -> Tuple[pathlib.Path, os.stat_result, bool]:
+        """Return the file path, stat result, and gzip status.
+
+        This method should be called from a thread executor
+        since it calls os.stat which may block.
+        """
         filepath = self._path
-
-        gzip = False
-        if "gzip" in request.headers.get(hdrs.ACCEPT_ENCODING, ""):
+        if check_for_gzipped_file:
             gzip_path = filepath.with_name(filepath.name + ".gz")
+            try:
+                return gzip_path, gzip_path.stat(), True
+            except OSError:
+                # Fall through and try the non-gzipped file
+                pass
 
-            if gzip_path.is_file():
-                filepath = gzip_path
-                gzip = True
+        return filepath, filepath.stat(), False
 
+    async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]:
         loop = asyncio.get_event_loop()
-        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)
+        check_for_gzipped_file = "gzip" in request.headers.get(hdrs.ACCEPT_ENCODING, "")
+        filepath, st, gzip = await loop.run_in_executor(
+            None, self._get_file_path_stat_and_gzip, check_for_gzipped_file
+        )
 
         etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}"
         last_modified = st.st_mtime

{"aiohttp/web_fileresponse.py": "import asyncio\nimport mimetypes\nimport os\nimport pathlib\nfrom typing import (  # noqa\n    IO,\n    TYPE_CHECKING,\n    Any,\n    Awaitable,\n    Callable,\n    Final,\n    Iterator,\n    List,\n    Optional,\n    Tuple,\n    Union,\n    cast,\n)\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import ETAG_ANY, ETag, must_be_empty_body\nfrom .typedefs import LooseHeaders, PathLike\nfrom .web_exceptions import (\n    HTTPNotModified,\n    HTTPPartialContent,\n    HTTPPreconditionFailed,\n    HTTPRequestRangeNotSatisfiable,\n)\nfrom .web_response import StreamResponse\n\n__all__ = (\"FileResponse\",)\n\nif TYPE_CHECKING:\n    from .web_request import BaseRequest\n\n\n_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]\n\n\nNOSENDFILE: Final[bool] = bool(os.environ.get(\"AIOHTTP_NOSENDFILE\"))\n\n\nclass FileResponse(StreamResponse):\n    \"\"\"A response object can be used to send files.\"\"\"\n\n    def __init__(\n        self,\n        path: PathLike,\n        chunk_size: int = 256 * 1024,\n        status: int = 200,\n        reason: Optional[str] = None,\n        headers: Optional[LooseHeaders] = None,\n    ) -> None:\n        super().__init__(status=status, reason=reason, headers=headers)\n\n        self._path = pathlib.Path(path)\n        self._chunk_size = chunk_size\n\n    async def _sendfile_fallback(\n        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        # To keep memory usage low,fobj is transferred in chunks\n        # controlled by the constructor's chunk_size argument.\n\n        chunk_size = self._chunk_size\n        loop = asyncio.get_event_loop()\n\n        await loop.run_in_executor(None, fobj.seek, offset)\n\n        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)\n        while chunk:\n            await writer.write(chunk)\n            count = count - chunk_size\n            if count <= 0:\n                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: IO[Any], offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        assert transport is not None\n\n        try:\n            await loop.sendfile(transport, fobj, offset, count)\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(etag.value == etag_value for etag in etags if not etag.is_weak)\n\n    async def _not_modified(\n        self, request: \"BaseRequest\", etag_value: str, last_modified: float\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPNotModified.status_code)\n        self._length_check = False\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = last_modified  # type: ignore[assignment]\n        # Delete any Content-Length headers provided by user. HTTP 304\n        # should always have empty response body\n        return await super().prepare(request)\n\n    async def _precondition_failed(\n        self, request: \"BaseRequest\"\n    ) -> Optional[AbstractStreamWriter]:\n        self.set_status(HTTPPreconditionFailed.status_code)\n        self.content_length = 0\n        return await super().prepare(request)\n\n    async def prepare(self, request: \"BaseRequest\") -> Optional[AbstractStreamWriter]:\n        filepath = self._path\n\n        gzip = False\n        if \"gzip\" in request.headers.get(hdrs.ACCEPT_ENCODING, \"\"):\n            gzip_path = filepath.with_name(filepath.name + \".gz\")\n\n            if gzip_path.is_file():\n                filepath = gzip_path\n                gzip = True\n\n        loop = asyncio.get_event_loop()\n        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)\n\n        etag_value = f\"{st.st_mtime_ns:x}-{st.st_size:x}\"\n        last_modified = st.st_mtime\n\n        # https://tools.ietf.org/html/rfc7232#section-6\n        ifmatch = request.if_match\n        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):\n            return await self._precondition_failed(request)\n\n        unmodsince = request.if_unmodified_since\n        if (\n            unmodsince is not None\n            and ifmatch is None\n            and st.st_mtime > unmodsince.timestamp()\n        ):\n            return await self._precondition_failed(request)\n\n        ifnonematch = request.if_none_match\n        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        modsince = request.if_modified_since\n        if (\n            modsince is not None\n            and ifnonematch is None\n            and st.st_mtime <= modsince.timestamp()\n        ):\n            return await self._not_modified(request, etag_value, last_modified)\n\n        if hdrs.CONTENT_TYPE not in self.headers:\n            ct, encoding = mimetypes.guess_type(str(filepath))\n            if not ct:\n                ct = \"application/octet-stream\"\n            should_set_ct = True\n        else:\n            encoding = \"gzip\" if gzip else None\n            should_set_ct = False\n\n        status = self._status\n        file_size = st.st_size\n        count = file_size\n\n        start = None\n\n        ifrange = request.if_range\n        if ifrange is None or st.st_mtime <= ifrange.timestamp():\n            # If-Range header check:\n            # condition = cached date >= last modification date\n            # return 206 if True else 200.\n            # if False:\n            #   Range header would not be processed, return 200\n            # if True but Range header missing\n            #   return 200\n            try:\n                rng = request.http_range\n                start = rng.start\n                end = rng.stop\n            except ValueError:\n                # https://tools.ietf.org/html/rfc7233:\n                # A server generating a 416 (Range Not Satisfiable) response to\n                # a byte-range request SHOULD send a Content-Range header field\n                # with an unsatisfied-range value.\n                # The complete-length in a 416 response indicates the current\n                # length of the selected representation.\n                #\n                # Will do the same below. Many servers ignore this and do not\n                # send a Content-Range header with HTTP 416\n                self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                return await super().prepare(request)\n\n            # If a range request has been made, convert start, end slice\n            # notation into file pointer offset and count\n            if start is not None or end is not None:\n                if start < 0 and end is None:  # return tail of file\n                    start += file_size\n                    if start < 0:\n                        # if Range:bytes=-1000 in request header but file size\n                        # is only 200, there would be trouble without this\n                        start = 0\n                    count = file_size - start\n                else:\n                    # rfc7233:If the last-byte-pos value is\n                    # absent, or if the value is greater than or equal to\n                    # the current length of the representation data,\n                    # the byte range is interpreted as the remainder\n                    # of the representation (i.e., the server replaces the\n                    # value of last-byte-pos with a value that is one less than\n                    # the current length of the selected representation).\n                    count = (\n                        min(end if end is not None else file_size, file_size) - start\n                    )\n\n                if start >= file_size:\n                    # HTTP 416 should be returned in this case.\n                    #\n                    # According to https://tools.ietf.org/html/rfc7233:\n                    # If a valid byte-range-set includes at least one\n                    # byte-range-spec with a first-byte-pos that is less than\n                    # the current length of the representation, or at least one\n                    # suffix-byte-range-spec with a non-zero suffix-length,\n                    # then the byte-range-set is satisfiable. Otherwise, the\n                    # byte-range-set is unsatisfiable.\n                    self.headers[hdrs.CONTENT_RANGE] = f\"bytes */{file_size}\"\n                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)\n                    return await super().prepare(request)\n\n                status = HTTPPartialContent.status_code\n                # Even though you are sending the whole file, you should still\n                # return a HTTP 206 for a Range request.\n                self.set_status(status)\n\n        if should_set_ct:\n            self.content_type = ct  # type: ignore[assignment]\n        if encoding:\n            self.headers[hdrs.CONTENT_ENCODING] = encoding\n        if gzip:\n            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING\n\n        self.etag = etag_value  # type: ignore[assignment]\n        self.last_modified = st.st_mtime  # type: ignore[assignment]\n        self.content_length = count\n\n        self.headers[hdrs.ACCEPT_RANGES] = \"bytes\"\n\n        real_start = cast(int, start)\n\n        if status == HTTPPartialContent.status_code:\n            self.headers[hdrs.CONTENT_RANGE] = \"bytes {}-{}/{}\".format(\n                real_start, real_start + count - 1, file_size\n            )\n\n        # If we are sending 0 bytes calling sendfile() will throw a ValueError\n        if count == 0 or must_be_empty_body(request.method, self.status):\n            return await super().prepare(request)\n\n        fobj = await loop.run_in_executor(None, filepath.open, \"rb\")\n        if start:  # be aware that start could be None or int=0 here.\n            offset = start\n        else:\n            offset = 0\n\n        try:\n            return await self._sendfile(request, fobj, offset, count)\n        finally:\n            await asyncio.shield(loop.run_in_executor(None, fobj.close))\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:** FileResponse Raises FileNotFoundError When Gzip Encoding is Requested but File is Missing

**Description:**
When a client includes `"gzip"` in the `Accept-Encoding` header, and the server does not have the corresponding gzipped file available, the `FileResponse` performs blocking file operations within the event loop. This results in the event loop being blocked and a `FileNotFoundError` being raised, causing the server to fail to respond properly.

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

async def handler(request):
    return web.FileResponse('logo.png.gz')  # Gzipped file does not exist

app = web.Application()
app.router.add_get('/logo.png', handler)

# Simulate a request with gzip encoding without the gzipped file available
async def test_prepare():
    request = make_mocked_request('GET', '/logo.png', headers={'Accept-Encoding': 'gzip'})
    file_response = FileResponse(Path('logo.png.gz'))
    file_response._path = Path('logo.png.gz')
    file_response._sendfile = make_mocked_coro(None)
    await file_response.prepare(request)
    # Expected: Gracefully handle missing gzip file
    # Actual: Raises FileNotFoundError due to blocking I/O
```

**Expected Behavior:**
When a gzipped file is requested via the `Accept-Encoding` header but the file does not exist, `FileResponse` should handle the situation gracefully without blocking the event loop or raising an uncaught exception. It should either serve the non-gzipped version of the file or respond with an appropriate error message.

**Actual Behavior:**
The `FileResponse` performs blocking I/O operations within the event loop, leading to the event loop being blocked and a `FileNotFoundError` being raised when the gzipped file is not found. This results in failed responses instead of handling the missing gzipped file gracefully.

[/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_fileresponse.py
import asyncio
import mimetypes
import os
import pathlib
from typing import (  # noqa
    IO,
    TYPE_CHECKING,
    Any,
    Awaitable,
    Callable,
    Final,
    Iterator,
    List,
    Optional,
    Tuple,
    Union,
    cast,
)

from . import hdrs
from .abc import AbstractStreamWriter
from .helpers import ETAG_ANY, ETag, must_be_empty_body
from .typedefs import LooseHeaders, PathLike
from .web_exceptions import (
    HTTPNotModified,
    HTTPPartialContent,
    HTTPPreconditionFailed,
    HTTPRequestRangeNotSatisfiable,
)
from .web_response import StreamResponse

__all__ = ("FileResponse",)

if TYPE_CHECKING:
    from .web_request import BaseRequest


_T_OnChunkSent = Optional[Callable[[bytes], Awaitable[None]]]


NOSENDFILE: Final[bool] = bool(os.environ.get("AIOHTTP_NOSENDFILE"))


class FileResponse(StreamResponse):
    """A response object can be used to send files."""

    def __init__(
        self,
        path: PathLike,
        chunk_size: int = 256 * 1024,
        status: int = 200,
        reason: Optional[str] = None,
        headers: Optional[LooseHeaders] = None,
    ) -> None:
        super().__init__(status=status, reason=reason, headers=headers)

        self._path = pathlib.Path(path)
        self._chunk_size = chunk_size

    async def _sendfile_fallback(
        self, writer: AbstractStreamWriter, fobj: IO[Any], offset: int, count: int
    ) -> AbstractStreamWriter:
        # To keep memory usage low,fobj is transferred in chunks
        # controlled by the constructor's chunk_size argument.

        chunk_size = self._chunk_size
        loop = asyncio.get_event_loop()

        await loop.run_in_executor(None, fobj.seek, offset)

        chunk = await loop.run_in_executor(None, fobj.read, chunk_size)
        while chunk:
            await writer.write(chunk)
            count = count - chunk_size
            if count <= 0:
                break
            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))

        await writer.drain()
        return writer

    async def _sendfile(
        self, request: "BaseRequest", fobj: IO[Any], offset: int, count: int
    ) -> AbstractStreamWriter:
        writer = await super().prepare(request)
        assert writer is not None

        if NOSENDFILE or self.compression:
            return await self._sendfile_fallback(writer, fobj, offset, count)

        loop = request._loop
        transport = request.transport
        assert transport is not None

        try:
            await loop.sendfile(transport, fobj, offset, count)
        except NotImplementedError:
            return await self._sendfile_fallback(writer, fobj, offset, count)

        await super().write_eof()
        return writer

    @staticmethod
    def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool:
        if len(etags) == 1 and etags[0].value == ETAG_ANY:
            return True
        return any(etag.value == etag_value for etag in etags if not etag.is_weak)

    async def _not_modified(
        self, request: "BaseRequest", etag_value: str, last_modified: float
    ) -> Optional[AbstractStreamWriter]:
        self.set_status(HTTPNotModified.status_code)
        self._length_check = False
        self.etag = etag_value  # type: ignore[assignment]
        self.last_modified = last_modified  # type: ignore[assignment]
        # Delete any Content-Length headers provided by user. HTTP 304
        # should always have empty response body
        return await super().prepare(request)

    async def _precondition_failed(
        self, request: "BaseRequest"
    ) -> Optional[AbstractStreamWriter]:
        self.set_status(HTTPPreconditionFailed.status_code)
        self.content_length = 0
        return await super().prepare(request)

    async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter]:
        filepath = self._path

        gzip = False
        if "gzip" in request.headers.get(hdrs.ACCEPT_ENCODING, ""):
            gzip_path = filepath.with_name(filepath.name + ".gz")

            if gzip_path.is_file():
                filepath = gzip_path
                gzip = True

        loop = asyncio.get_event_loop()
        st: os.stat_result = await loop.run_in_executor(None, filepath.stat)

        etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}"
        last_modified = st.st_mtime

        # https://tools.ietf.org/html/rfc7232#section-6
        ifmatch = request.if_match
        if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch):
            return await self._precondition_failed(request)

        unmodsince = request.if_unmodified_since
        if (
            unmodsince is not None
            and ifmatch is None
            and st.st_mtime > unmodsince.timestamp()
        ):
            return await self._precondition_failed(request)

        ifnonematch = request.if_none_match
        if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch):
            return await self._not_modified(request, etag_value, last_modified)

        modsince = request.if_modified_since
        if (
            modsince is not None
            and ifnonematch is None
            and st.st_mtime <= modsince.timestamp()
        ):
            return await self._not_modified(request, etag_value, last_modified)

        if hdrs.CONTENT_TYPE not in self.headers:
            ct, encoding = mimetypes.guess_type(str(filepath))
            if not ct:
                ct = "application/octet-stream"
            should_set_ct = True
        else:
            encoding = "gzip" if gzip else None
            should_set_ct = False

        status = self._status
        file_size = st.st_size
        count = file_size

        start = None

        ifrange = request.if_range
        if ifrange is None or st.st_mtime <= ifrange.timestamp():
            # If-Range header check:
            # condition = cached date >= last modification date
            # return 206 if True else 200.
            # if False:
            #   Range header would not be processed, return 200
            # if True but Range header missing
            #   return 200
            try:
                rng = request.http_range
                start = rng.start
                end = rng.stop
            except ValueError:
                # https://tools.ietf.org/html/rfc7233:
                # A server generating a 416 (Range Not Satisfiable) response to
                # a byte-range request SHOULD send a Content-Range header field
                # with an unsatisfied-range value.
                # The complete-length in a 416 response indicates the current
                # length of the selected representation.
                #
                # Will do the same below. Many servers ignore this and do not
                # send a Content-Range header with HTTP 416
                self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}"
                self.set_status(HTTPRequestRangeNotSatisfiable.status_code)
                return await super().prepare(request)

            # If a range request has been made, convert start, end slice
            # notation into file pointer offset and count
            if start is not None or end is not None:
                if start < 0 and end is None:  # return tail of file
                    start += file_size
                    if start < 0:
                        # if Range:bytes=-1000 in request header but file size
                        # is only 200, there would be trouble without this
                        start = 0
                    count = file_size - start
                else:
                    # rfc7233:If the last-byte-pos value is
                    # absent, or if the value is greater than or equal to
                    # the current length of the representation data,
                    # the byte range is interpreted as the remainder
                    # of the representation (i.e., the server replaces the
                    # value of last-byte-pos with a value that is one less than
                    # the current length of the selected representation).
                    count = (
                        min(end if end is not None else file_size, file_size) - start
                    )

                if start >= file_size:
                    # HTTP 416 should be returned in this case.
                    #
                    # According to https://tools.ietf.org/html/rfc7233:
                    # If a valid byte-range-set includes at least one
                    # byte-range-spec with a first-byte-pos that is less than
                    # the current length of the representation, or at least one
                    # suffix-byte-range-spec with a non-zero suffix-length,
                    # then the byte-range-set is satisfiable. Otherwise, the
                    # byte-range-set is unsatisfiable.
                    self.headers[hdrs.CONTENT_RANGE] = f"bytes */{file_size}"
                    self.set_status(HTTPRequestRangeNotSatisfiable.status_code)
                    return await super().prepare(request)

                status = HTTPPartialContent.status_code
                # Even though you are sending the whole file, you should still
                # return a HTTP 206 for a Range request.
                self.set_status(status)

        if should_set_ct:
            self.content_type = ct  # type: ignore[assignment]
        if encoding:
            self.headers[hdrs.CONTENT_ENCODING] = encoding
        if gzip:
            self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING

        self.etag = etag_value  # type: ignore[assignment]
        self.last_modified = st.st_mtime  # type: ignore[assignment]
        self.content_length = count

        self.headers[hdrs.ACCEPT_RANGES] = "bytes"

        real_start = cast(int, start)

        if status == HTTPPartialContent.status_code:
            self.headers[hdrs.CONTENT_RANGE] = "bytes {}-{}/{}".format(
                real_start, real_start + count - 1, file_size
            )

        # If we are sending 0 bytes calling sendfile() will throw a ValueError
        if count == 0 or must_be_empty_body(request.method, self.status):
            return await super().prepare(request)

        fobj = await loop.run_in_executor(None, filepath.open, "rb")
        if start:  # be aware that start could be None or int=0 here.
            offset = start
        else:
            offset = 0

        try:
            return await self._sendfile(request, fobj, offset, count)
        finally:
            await asyncio.shield(loop.run_in_executor(None, fobj.close))

```
--- 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